OleDbDataReader retrieve specific row values (CSHTML) - c#

I'm trying to pull values out of a reader in a particular order. I have a number of reps and need to group them together into their regions so I can sub total them.
I have a connection to a local mdb, which works fine for the time being. And in other places I loop the reader to get data out. Like so...
var thisConnection = new OleDbConnection(#"Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C:\SALES.mdb");
thisConnection.Open();
var thisCommand = thisConnection.CreateCommand();
thisCommand.CommandText = "SELECT `Rep Name`, Sum(`Budget`) AS xyValues_Bud FROM `Invoices` GROUP BY `Rep Name`;";
OleDbDataReader thisReader = thisCommand.ExecuteReader();
.....
#while(thisReader.Read())
{
#thisReader["Rep Name"] <text>,</text> #thisReader["xyValues_Bud "]
}
However I want to pull these out in an order so lets say... SouthRep, EastRep, WestRep, NorthRep
So I need something like...
#thisReader["Rep Name"] = "SouthRep" {<text>SouthRep,</text> #thisReader["xyValues_Bud "] }
#thisReader["Rep Name"] = "EastRep" {<text>EastRep,</text> #thisReader["xyValues_Bud "] }
....
I've thought about creating a loop and just picking out the entries I want but there must be a simpler way.
Thank you

Related

SQL reader cannot read all the checkListBox checkedItems

I am working on a windows form application using c#. I have a problem with trying to match the checked data from my checkListBox to my sql data checkbox column. What I am trying to do is a binded the checkListBox to one of my column in my database table let's call it employee_detail which can fetch the data from database correctly. However, when I am trying to export the checked items from that checklistbox, I can only read the first checked item from the checklistbox. If I have 3 items and I checked all 3 items, my excel only show the 1st checked items. I have no idea why. For example, I have Amy, Peter and Jimmy in my checkListBox. When I select Amy, it will export the name Amy to my excel table. However, if I select all 3 (Amy, Peter and Jimmy), it only export Amy to my excel even though I have 3 checked. I have no idea why it doesn't work.
What I tried first time. This is a complete disaster. The HasRow in my datareader cannot fetch any column from my db even I can execute the same query in my sql console and find the column I am looking for. This loop skip whatever it is inside the while loop since HasRow = false
Here is my first attempted
foreach(var items in checkListBox1.CheckedItems){
string query = "select * from my_table WHERE employeeName = '"+items+"'"
SqlCommand myCommand = new SqlCommand(query, myConn);
SqlDataReader dr = myCommand.ExecuteReader();
while(dr.Read()){
//read the column
}
}
My second attempt is using the parameters, this seems to work, but the problems (what I suggested above) is my excel files only showed the first checked items (I believe it only loop through the while loop once), when my db table has more then one record
Here is my second attempted
foreach (var item in employeeCheckListBox.CheckedItems)
{
if (items.Length == 0)
{
items = item.ToString();
}
else
{
items = items + "," + item;
}
string query = #"select * from [employeeId_detail] as td LEFT JOIN[order_detail] as cd ON td.employeeId = cd.employeeId Where employeeName in (#name)";
myCommand = new SqlCommand(query, myConn);
myCommand.Parameters.Add("#name", SqlDbType.NVarChar);
myCommand.Parameters["#name"].Value = items;
SqlDataReader dr = myCommand.ExecuteReader();
while (dr.Read())
{
i++;
{
string f_Name = dr.GetString(0);
string l_Name = dr.GetString(1);
string full_Name = dr.GetString(2);
string e_email = dr.GetString(3);
xlEmployeeDetail.Cells[i, 1] = f_Name;
xlEmployeeDetail.Cells[i, 2] = l_Name;
xlEmployeeDetail.Cells[i, 3] = full_Name;
xlEmployeeDetail.Cells[i, 4] = e_email;
}
}
dr.Close();
}
Reference to our chat: Problem is not in the code block that you wrote. The problem lies within the characters that you are using for the fetching the content out of DB.
As we tested, this block of code works fine with the English characters but fails to Chinese characters which is all together a separate problem.
However if you still would go for the Chinese character then probably you would need to do settings at the DB level, and your same code will work fine.
Parameterizing is definitely the better way to go, like in your second example.
The problem is, I'm fairly certain your query ends up looking like this, where you're passing it a single name with a couple commas in it, not a list of separate names:
select *
from [employeeId_detail] as td
LEFT JOIN[order_detail] as cd ON td.employeeId = cd.employeeId
Where employeeName in ('joe, bob, sue')
Instead, here's something a bit more dynamic, creating a parameter for each checked item.
string parameterList = "";
int parameterCounter = 0;
// Add a parameter placeholder for each "checked" item
for (var i=0; i<checkListBox1.CheckedItems.Count; i++)
parameterList += (i==0) ? "#p" + i : ", #p" + i;
string query = #"select *
from [employeeId_detail] as td
LEFT JOIN[order_detail] as cd ON td.employeeId = cd.employeeId
Where employeeName in (" + parameterList + ")";
myCommand = new SqlCommand(query, myConn);
// Now give each parameter a value from the collection of checked items
for (var i = 0; i < checkListBox1.CheckedItems.Count; i++)
myCommand.Parameters.AddWithValue("#p" + i, checkListBox1.CheckedItems[i].ToString());
SqlDataReader dr = myCommand.ExecuteReader();

Error with databound DataGridView Display Textbox's Text

I have a datagridview (dgvSelectedItem)and I want it to display some values from textboxes but I have this error
Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound
My code is:
DataTable dt = new DataTable();
string conn = "server=.;uid=sa;pwd=123;database=PharmacyDB;";
Okbtn()
{
SqlDataAdapter da = new SqlDataAdapter("select UnitPrice from ProductDetails where prdctName like '"+txtSelectedName.Text + "'", conn);
da.Fill(dt);
dgvSelectedItem.DataSource = dt;
//this code work but when I add these lines the Error appear
dgvSelectedItem.Rows.Add(txtSelectedName.Text);
dgvSelectedItem.Rows.Add(txtSelectedQnty.Text); }
Thanks in advance
UPDATED Per OP Comment
So you want a user to enter the product name and quantity, then to run a query against the database to retrieve the unit price information. Then you want to display all of that information to your user in a DataGridView control.
Here is what I suggest:
Include the prdctName field in your SELECT clause.
If you want to bind all relevant data to a DataGridView including the Quantity variable and your TotalPrice calculation, then include them in your SELECT statement. When you bind data to the control from an SQL query like this, the result set is mapped to the grid. In other words, if you want information to be displayed when setting the DataSource property then you need to include the information in your SQL result set.
Don't use LIKE to compare for prdctName as it slightly obscures the purpose of your query and instead just use the = operator.
In addition from a table design perspective, I would add a UNIQUE INDEX on the prdctName column if it is indeed unique in your table - which I sense it likely is. This will improve performance of queries like the one you are using.
Here is what your SQL should look like now:
SELECT prdctName, UnitPrice, #Quantity AS Quantity,
UnitPrice * #Quantity AS Total_Price
FROM ProductDetails
WHERE prdctName = #prdctName
A few other suggestions would be to use prepared SQL statements and .NET's using statement as already noted by Soner Gönül. I'll try to give you motivation for applying these techniques.
Why should I use prepared SQL statements?
You gain security against SQL Injection attacks and a performance boost as prepared statements (aka parameterized queries) are compiled and optimized once.
Why should I use the using statement in .NET?
It ensures that the Dispose() method is called on the object in question. This will release the unmanaged resources consumed by the object.
I wrote a little test method exemplifying all of this:
private void BindData(string productName, int quantity)
{
var dataTable = new DataTable();
string sql = "SELECT prdctName, UnitPrice, #Quantity AS Quantity, " +
"UnitPrice * #Quantity AS Total_Price " +
"FROM ProductDetails " +
"WHERE prdctName = #prdctName";
using (var conn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#prdctName", productName);
cmd.Parameters.AddWithValue("#Quantity", quantity);
using (var adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(dataTable);
dataGridView1.DataSource = dataTable;
}
}
}
}
Here's a sample input and output of this method:
BindData("Lemon Bars", 3);
I searched over the internet and looks like there is no way to add new rows programmatically when you set your DataSource property of your DataGridView.
Most common way is to add your DataTable these values and then bind it to DataSource property like:
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
Also conn is your connection string, not an SqlConnection. Passing SqlConnection as a second parameter in your SqlDataAdapter is a better approach in my opinion.
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Finally, don't forget to use using statement to dispose your SqlConnection and SqlDataAdapter.
I assume you want to use your text as %txtSelectedName.Text%, this is my example;
DataTable dt = new DataTable();
using(SqlConnection conn = new SqlConnection("server=.;uid=sa;pwd=123;database=PharmacyDB;"))
using(SqlCommand cmd = new SqlCommand("select UnitPrice from ProductDetails where prdctName like #param"))
{
cmd.Connection = conn;
cmd.Parameter.AddWithValue("#param", "%" + txtSelectedName.Text + "%");
using(SqlDataAdapter da = new SqlDataAdapter(cmd, conn))
{
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
}
}

store values from database into combobox

I'm trying to write a program using SQL and OleDB and I get an error while the Program is running.
the program first counts the number of rows in the table(access table which called 'tblCodons')
and storage the number as integer in j.
then the program stores all the rows (from a specific column which called 'codonsFullName') in comboBox1.
the code is below
I get this ERROR:System.Data.OleDb.OleDbException (0x80040E14): Invalid SQL statement;Required values​​' DELETE ',' INSERT ',' PROCEDURE ',' SELECT 'or' UPDATE
the code:
int j=0;
OleDbConnection conn1 = new OleDbConnection(connectionString);
conn1.Open();
string sqlCount= "SET #j= SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
OleDbCommand counter = new OleDbCommand(sqlCount, conn1);
counter.ExecuteNonQuery();
conn1.Close();
OleDbConnection conn2 = new OleDbConnection(connectionString);
conn2.Open();
string sqlFill = "SELECT tblCodons.codonsFullName FROM tblCodons";
OleDbCommand fill = new OleDbCommand(sqlFill, conn2);
fill.ExecuteNonQuery();
OleDbDataReader dataReader = fill.ExecuteReader();
dataReader.Read();
for (int i = 0; i < j; i++)
{
comboBox1.Items.Add(dataReader.GetString(i));
}
You seem to need the count only for the loop. Also I do not understand why you are executing fill.ExecuteNonQuery() before executing is as a reader.
Also setting #j (if it did work) in a sql query has no scope to a local variable j in the code you are trying to set.
You should only need the following code (apologies for any syntax errors)
OleDbConnection conn2 = new OleDbConnection(connectionString);
conn2.Open();
string sqlFill = "SELECT tblCodons.codonsFullName FROM tblCodons";
OleDbCommand fill = new OleDbCommand(sqlFill, conn2);
OleDbDataReader dataReader = fill.ExecuteReader();
int j = 0;
if (dataReader.HasRows)
{
while(dataReader.Read())
{
comboBox1.Items.Add(dataReader.GetString(0));
j++;
}
}
Hope that helps
Leaving this answer here as an explanation for fixing your code as it currently exists, but also want to point out that I recommend going with Kamal's solution; it only queries the database once.
This line is probably your error:
string sqlCount= "SET #j= SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
change to
string sqlCount= "SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
You'll want to change your code to obtain the result of that first query like this:
j = counter.ExecuteScalar();
First, as Kamal Mentioned you can't directly set a variable from a sql query as you are trying to do and as the exception states only "SELECT" , "INSERT","UPDATE" and "DELETE" commands can be use in a query.
Second, I don't know why you need to get the record counts before getting the actual data but if it's really necessary you can write your query like this :
var query="SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons;SELECT tblCodons.codonsFullName FROM tblCodons;";
Then you can execute both query using a single DataReader. When you execute DataReader.ExequteQuery() it will contain two results.the first one has access to the count and the second one has access to actual data.
Here's an example

in C# OleDbDataAdapter.fill method not giving any data or error

I am using a data adapter to pull data from an access database (see below code). When I run the SQL in the Access database I get the expected data. However when I step through the code the fill method produces only the table definition but no rows.
I have used this procedure many times in the past and it still works for those calls.
Again the SQL in access returns the correct data and in C# I don't get ANY error message but I don't get the data either. Had anyone seen this before?
`
public void GetQueries(ref DataTable tSQL, String tool, string Filter, OleDbConnection lConn)
{
OleDbDataAdapter dadapt = new OleDbDataAdapter(); //Data Adapter for Access
String lSQL = "";
//assign the connection to the processing mdb
//lAccProcSQL.Connection = lConn;
//Pull the queries to be executed
lSQL = "SELECT * FROM tblSQL WHERE Active = TRUE AND ToolCode = '" +
tool + "' and type not in (" + Filter + ") ORDER BY QueryNum";
//Set the adapter to point to the tblSQL table
dadapt = new OleDbDataAdapter(lSQL, lConn);
//clear tables in case of rerun
tSQL.Clear();
//Fill working queries data table
dadapt.Fill(tSQL);
}`
Are you sure that the filter that you've defined in the WHERE clause will evaluate to true on certain rows ?
Why don't you use parameters instead of string concatenation ? Are you sure that Active = True will evaluate to true ? As far as I know, True is represented by -1 in Access.
So, why don't you try it like this:
var command = new OleDbCommand();
command.Connection = lConn;
command.CommandText = "SELECT * FROM tblSql WHERE Active = -1 AND ToolCode = #p_toolCode AND type NOT IN (" + filter + ") ORDER BY querynum";
command.Parameters.Add ("#p_toolCode", OleDbType.String).Value = tool;
datapt = new OleDbDataAdapter();
datapt.SelectCommand = command;
dadapt.Fill (tSql);

How do I count the number of rows returned in my SQLite reader in C#?

I'm working in Microsoft Visual C# 2008 Express and with SQLite.
I'm querying my database with something like this:
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "select id from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();
Then I do something like this:
if (reader.HasRows == true) {
while (reader.Read()) {
// I do stuff here
}
}
What I want to do is count the number of rows before I do "reader.Read()" since the number returned will affect what I want/need to do. I know I can add a count within the while statement, but I really need to know the count before.
Any suggestions?
The DataReader runs lazily, so it doesn't pick up the entirety of the rowset before beginning. This leaves you with two choices:
Iterate through and count
Count in the SQL statement.
Because I'm more of a SQL guy, I'll do the count in the SQL statement:
cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
int RowCount = 0;
RowCount = Convert.ToInt32(cmd.ExecuteScalar());
cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();
//...
Note how I counted *, not id in the beginning. This is because count(id) will ignore id's, while count(*) will only ignore completely null rows. If you have no null id's, then use count(id) (it's a tad bit faster, depending on your table size).
Update: Changed to ExecuteScalar, and also count(id) based on comments.
What you request is not feasible -- to quote Igor Tandetnik, my emphasis:
SQLite produces records one by one, on request, every time you call sqlite3_step.
It simply doesn't know how many there are going to be, until on some sqlite3_step
call it discovers there are no more.
(sqlite3_step is the function in SQLite's C API that the C# interface is calling here for each row in the result).
You could rather do a "SELECT COUNT(*) from myTable where word = '" + word + "';" first, before your "real" query -- that will tell you how many rows you're going to get from the real query.
Do a second query:
cmd.CommandText = "select count(id) from myTable where word = '" + word + "';";
cmd.CommandType = CommandType.Text;
SQLiteDataReader reader = cmd.ExecuteReader();
Your reader will then contain a single row with one column containing the number of rows in the result set. The count will have been performed on the server, so it should be nicely quick.
If you are only loading an id column from the database, would it not be easier to simply load into a List<string> and then work from there in memory?
Normally i would do
select count(1) from myTable where word = '" + word + "';";
to get the result as fast as possible. In the case where id is an int then it won't make much difference. If it was something a bit bigger like a string type then you'll notice a difference over a large dataset.
Reasoning about it count(1) will include the null rows. But i'm prepared to be corrected if i'm wrong about that.
Try this,
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();
while (reader.HasRows)
reader.Read();
int total_rows_in_resultset = reader.StepCount;
total_rows_in_resultset gives you the number of rows in resultset after processing query
remember that if you wanna use the same reader then close this reader and start it again.
Here is my full implementation in a static method.
You should be able to plug this into your class (replace _STR_DB_FILENAME & STR_TABLE_NAME with your database file name and table name).
/// <summary>
/// Returns a count of the number of items in the database.
/// </summary>
/// <returns></returns>
public static int GetNumberOfItemsInDB()
{
// Initialize the count variable to be returned
int count = 0;
// Start connection to db
using (SqliteConnection db =
new SqliteConnection("Filename=" + _STR_DB_FILENAME))
{
// open connection
db.Open();
SqliteCommand queryCommand = new SqliteCommand();
queryCommand.Connection = db;
// Use parameterized query to prevent SQL injection attacks
queryCommand.CommandText = "SELECT COUNT(*) FROM " + _STR_TABLE_NAME;
// Execute command and convert response to Int
count = Convert.ToInt32(queryCommand.ExecuteScalar());
// Close connection
db.Close();
}
// return result(count)
return count;
}
Note: To improve performance, you can replace '' in "SELECT COUNT()…." with the column name of the primary key in your table for much faster performance on larger datasets.
but I really need to know the count before
Why is that ? this is usually not necessary, if you use adequate in-memory data structures (Dataset, List...). There is probably a way to do what you want that doesn't require to count the rows beforehand.
You do have to count with select count... from...
This will make your application slower. However there is an easy way to make your app faster and that way is using parameterized queries.
See here: How do I get around the "'" problem in sqlite and c#?
(So besides speed parameterized queries have 2 other advantages too.)
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "select id from myTable where word = '" + word + "';";
SQLiteDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
total_rows_in_resultset++;
}
Surely a better way to get a row count would be something like this:-
SQLiteDataReader reader = SendReturnSQLCommand(dbConnection, "SELECT COUNT(*) AS rec_count FROM table WHERE field = 'some_value';");
if (reader.HasRows) {
reader.Read();
int count = Convert.ToInt32(reader["rec_count"].ToString());
...
}
That way you don't have to iterate over the rows

Categories