SQL reader cannot read all the checkListBox checkedItems - c#

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();

Related

Retrieving more than one value from database and store them

What I'm trying to do is retrieve the FullName values where the Username corresponds to the user, which does indeed work, the problem is I don't exactly know how to store the values when there is more than one value, I tried using an array but when there is for example two values, when retrieving it, characterReader[0] will be null and characterReader[1] will have only the first retrieved value, however if there is only 1 value to be retrieve characterReader[0] will no longer be null and display the correct value.
This is my code, I'm not exactly sure this is even the right way:
SqlCommand displayCharactersCMD = new SqlCommand(String.Format("SELECT FullName FROM [Characters] WHERE Username='{0}'", username), con);
displayCharactersCMD.Parameters.AddWithValue("#checkPlayerName", username);
using (SqlDataReader reader = displayCharactersCMD.ExecuteReader())
{
int counter = 0;
while (reader.Read())
{
if (counter != countCharsToVar)
{
characterReader = new string[countCharsToVar];
characterReader[counter] = reader[0].ToString();
counter++;
}
else
break;
}
}
Example when there are two values to be retrieved:
API.consoleOutput("CHAR 1: " + characterReader[0]); - This will become null.
API.consoleOutput("CHAR 2: " + characterReader[1]); - This will contain the first value.
How I intend it to work:
API.consoleOutput("CHAR 1: " + characterReader[0]); - This will display first value.
API.consoleOutput("CHAR 2: " + characterReader[1]); - This will display second value.
Instead of storing values in array, you can utilize List<>. This might help you:
SqlCommand displayCharactersCMD = new SqlCommand("SELECT FullName FROM [Characters] WHERE Username=#checkPlayerName");
displayCharactersCMD.Parameters.AddWithValue("#checkPlayerName", username);
var characterReader = new List<string>();
using (SqlDataReader reader = displayCharactersCMD.ExecuteReader())
{
while (reader.Read())
{
characterReader.Add(reader[0].ToString());
}
}
Here's a really good link from Microsoft: https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/retrieving-data-using-a-datareader
Use the GetString(columnNumber) method to get the whole value from the row. That should make it easy for you.
Hope this helps.
You need to iterate SqlDataReader for each value, calling reader.Read() makes the reader to point to the next row, when it reaches the end and there is no more rows in the resultset it returns false.
When a "read" is done, the reader is moved to point to the next row so you can access all the columns in this way. reader[0] will be the first column, reader[1] for the second column and so on, in your example you only have one column fullname.
You can add all your results to a list in this way:
var values = new List<string>();
using (SqlDataReader reader = displayCharactersCMD.ExecuteReader())
{
while (reader.Read())
{
values.Add(reader[0]);
}
}
Note: as #steve points, parameter does not work that way, you remove the string.format call and use the name of the parameter.
new SqlCommand("SELECT FullName FROM [Characters] WHERE Username=#checkPlayerName"), con);

How to fill a c# windows form listview with datas from 2 tables?

I'm doing a "load" button to load up all datas from a SQL database, to put them in a listview.
if(lstEnsemble.Items.Count == 0)
{
MySqlConnection connexion = OpenConnection();
MySqlCommand reqRemplissageClient = new MySqlCommand("select * from client order by idClient ASC;", connexion);
MySqlCommand reqRemplissagePanne = new MySqlCommand("select * from Panne order by idClient ASC;", connexion);
MySqlDataReader resultatClient = reqRemplissageClient.ExecuteReader();
MySqlDataReader resultatPanne = reqRemplissageClient.ExecuteReader();
while (resultatClient.Read() && resultatPanne.Read())
{
ListViewItem item = new ListViewItem(resultatClient["nomCli"].ToString());
item.SubItems.Add(resultatClient["prenomCli"].ToString());
if (resultatClient["idClient"] == resultatPanne["idClient"])
{
item.SubItems.Add(resultatPanne["appareil"].ToString());
}
item.SubItems.Add(resultatClient["villeCli"] + " " + resultatClient["cpCli"] + " " + resultatClient["rueCli"]);
item.SubItems.Add(resultatClient["telCli"].ToString());
if (resultatClient["idClient"] == resultatPanne["idClient"])
{
item.SubItems.Add(resultatPanne["description"].ToString());
item.SubItems.Add(resultatPanne["dateEntree"].ToString());
}
item.SubItems.Add(resultatClient["mailCli"].ToString());
lstEnsemble.Items.Add(item);
}
CloseConnection(connexion);
}
This does not work, obviously, but I can't manage to find another way to do this.
I tried to do both of requests separately, but again I'm missing the logic to this.
In your question it's not fully clear what kind of data you are working with. It seems your presuming (or better hoping) that the idClient values match exactly in both tables.
I presume that you actually want to do a JOIN. If this is not true, I will delete this post again.
So you should do only one query using a JOIN:
if(lstEnsemble.Items.Count == 0)
{
string sql = #"SELECT nomCli, prenomCli, appareil, villeCli, rueCli,
cpCli, telCli, description, dateEntree, mailCli
FROM client LEFT JOIN Panne ON (client.idClient = Panne.idClient)";
MySqlConnection connexion = OpenConnection();
MySqlCommand request = new MySqlCommand(sql, connexion);
MySqlDataReader resultat = requestExecuteReader();
while (resultat.Read())
{
ListViewItem item = new ListViewItem(resultat["nomCli"].ToString());
item.SubItems.Add(resultat["prenomCli"].ToString());
item.SubItems.Add(resultat["appareil"].ToString());
item.SubItems.Add(resultat["villeCli"] + " " + resultat["cpCli"] + " " + resultat["rueCli"]);
item.SubItems.Add(resultat["telCli"].ToString());
item.SubItems.Add(resultat["description"].ToString());
item.SubItems.Add(resultat["dateEntree"].ToString());
item.SubItems.Add(resultat["mailCli"].ToString());
lstEnsemble.Items.Add(item);
}
CloseConnection(connexion);
}
But you should be aware that for rows in client with no matching rows in Panne there will be null values in the results. So you need to check for them first using resultat.IsDbNull() on the column index (position in sql query).
Alternativly you may use an INNER JOIN instead of a LEFT JOIN to get only rows that exist in both tables.
You should change a few things, then it becomes pretty easy.
Make a single SQL Query. Just JOIN your tables together and SELECT what you need.
Separate the Database access from filling your listview. Put your result in an array or datatable.
With your data available you can easily bind it or iterate through it to generate ListViewItems.

SELECT statement not returning anything

My issue is that the results are empty when executing the statement, even though when executing it in Microsoft's SQL server studio it works.
//This has two values in it (Ex: 4 and 2)
string[] arr2 = groupListValues.Split('-');
List<string> userID = new List<string>();
// Connect to the database
SqlConnection gconn = new SqlConnection(ConfigurationManager.ConnectionStrings["connectinfohere"].ConnectionString);
gconn.Open();
SqlCommand command1 = new SqlCommand();
command1.Connection = gconn;
String sql = "SELECT ID FROM Users WHERE Group = #groupID";
command1.CommandText = sql;
command1.Parameters.Add(new SqlParameter("#groupID", ""));
SqlDataReader reader = command1.ExecuteReader();
//issue is in this loop
foreach (string str in arr2)
{
command1.Parameters["#groupID"].Value = str;
while (reader.Read())
{
userID.Add(reader["ID"].ToString());
}
}
Not sure what the issue is. The "ID" I'm getting in the SQL statement is of type bigint, could that cause an issue?
The reason I am setting the parameter inside the foreach loop is because, for each value in arr2 corresponds to a group that several users could be attached to. So I need to loop through that, get the users attached to each groupID, then add all their ID's to a list.
There are two problems with you code:
The first one is that you setting the #groupID parameter after you execute the reader. To fix it, execute the reader after you set the parameter value like this:
foreach (string str in arr2)
{
command1.Parameters["#groupID"].Value = str;
using(SqlDataReader reader = command1.ExecuteReader())
{
while (reader.Read())
{
userID.Add(reader["ID"].ToString());
}
}
}
The second problem is that Group is a reserved keyword in SQL, so you need to wrap it with square brackets like this:
String sql = "SELECT ID FROM Users WHERE [Group] = #groupID";

OleDbDataReader retrieve specific row values (CSHTML)

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

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