Can't retrieve data using ExecuteReader() when grouping queries - c#

I need to retrieve the information stored in a database of some thousand items. If I go one by one by this way it takes a large amount of time (tac is a 8-character string):
string connectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=DataBase\IMEIDB.accdb";
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
connection.Open();
using (OleDbCommand command = connection.CreateCommand())
{
OleDbDataReader reader;
command.CommandText = "SELECT TAC, Name, Model, Year, Manufacturer, LTE FROM Terminales WHERE TAC = #tac";
command.Parameters.AddWithValue("#tac", tac);
reader = command.ExecuteReader();
while (reader.Read())
{
ulong tac = Convert.ToUInt64(reader.GetString(0));
if (this.DiccionarioTerminales.ContainsKey(tac))
{
DiccionarioTerminales[tac].inDB = true;
DiccionarioTerminales[tac].Name = reader.GetValue(1).ToString();
DiccionarioTerminales[tac].Manufacturer = reader.GetValue(2).ToString();
DiccionarioTerminales[tac].Model = reader.GetValue(3).ToString();
DiccionarioTerminales[tac].Year = reader.GetValue(4).ToString();
DiccionarioTerminales[tac].LTE = reader.GetValue(5).ToString();
}
}
command.Dispose();
}
connection.Dispose();
}
It works well (I know I must use ExecuteNonQuery() if it's only one record, but this example is only a test), but if I try to group the tac 10 by 10 (now tac is a string like 'xxxxxxxx','xxxxxxxx','xxxxxxxx','xxxxxxxx'...) with the next changes in my code...
OleDbDataReader reader;
command.CommandText = "SELECT TAC, Name, Model, Year, Manufacturer, LTE FROM Terminales WHERE TAC IN (#tac)";
command.Parameters.AddWithValue("#tac", tac);
It doesn't enter in the while loop and I don't know why...
Is there something that am I missing or maybe I need to use another method to retrieve those data?
EDIT: Change the format due to the Soner Gönül answer

Because when you use this string in IN clause, it will seems like;
IN (xxxxxxxx,xxxxxxxx,xxxxxxxx,xxxxxxxx)
but the right syntax should be
IN ('xxxxxxxx','xxxxxxxx','xxxxxxxx','xxxxxxxx')
That's why it doesn't work. One solution might be, you can split your string with ,, format them with using single quotes and join them , again.
var str = "xxxxxxxx,xxxxxxxx,xxxxxxxx,xxxxxxxx";
var result = string.Join(",", str.Split(',').Select(s => string.Format("'{0}'", s)));
result will be 'xxxxxxxx','xxxxxxxx','xxxxxxxx','xxxxxxxx' and you can use it in your IN clause like;
...TAC IN (#tac)
and
command.Parameters.AddWithValue("#tac", result);
Also don't use AddWithValue as much as you can. It may generate unexpected and surprising results sometimes. Use Add method overload to specify your parameter type and it's size.
Related: Parameterize an SQL IN clause

Related

OleDB Queries not Returning All Results

What I am expecting my code to do is gather the top ten records that have an 'I' in the SQLMigrationFl field, and then process those ten records by removing that 'I' value. Below is the portion of code that handles this.
string inProgressQuery = "SELECT TOP 10 IDType, ID, Program, Date, Body,
AcctNo, ANPACAcctID, ANPACClientID, TEAMID,
ImportDate, AnnualReview, TeamGUID,
ANPACClientLastName, ANPACClientFirstName, " +
"PolicyNumber, AccountOwnerLastName,
AccountOwnerFirstName, SCRACF, SCDateTime, NoteID
FROM NoteTable WHERE SQLMigrationFl = ?";
command = new OleDbCommand(inProgressQuery, connection);
command.Parameters.AddWithValue("SQLMigrationFl", "I");
reader = command.ExecuteReader();
if(reader.HasRows)
{
while(reader.Read())
{
//clear the In Progress flag
query = "UPDATE NoteTable SET SQLMigrationFl = ? WHERE
NoteTable.NoteID = " + reader[19].ToString();
command = new OleDbCommand(query, connection);
command.Parameters.AddWithValue("SQLMigrationFl", DBNull.Value);
reader = command.ExecuteReader();
}
}
What I am finding is that the query returns one value, and processes it. Then in five seconds it finds another record and reprocesses that one. *Five seconds is just a delay we have set in code to check for more records to be processed. It processes one record at a time, rather than grabbing ten and processing those at once within the same while loop. Is something wrong with my code or query?
Thanks for the help.
The culprit is your reassigning of the data reader using reader = command.ExecuteReader();. That will now return 1 result and your next loop will be over that 1 result. Regardless for non SELECT queries use ExecuteNonQuery instead.
Other places that you can update your code to be "better" are
Use parameters whenever you have values that you want to use in your sql statements.
Always specify the type for all your parameters.
Always wrap your types instances that implement IDisposable in using blocks to ensure resources are cleaned up.
I also recommend you not share connection instances, below it seems that there might be a static connection somewhere. It would be best to not share one and create/open one when you need it and then close/dispose it.
string inProgressQuery = "SELECT TOP 10 IDType, ID, Program, Date, Body,
AcctNo, ANPACAcctID, ANPACClientID, TEAMID,
ImportDate, AnnualReview, TeamGUID,
ANPACClientLastName, ANPACClientFirstName, " +
"PolicyNumber, AccountOwnerLastName,
AccountOwnerFirstName, SCRACF, SCDateTime, NoteID
FROM NoteTable WHERE SQLMigrationFl = ?";
using(var command = new OleDbCommand(inProgressQuery, connection))
{
// I guessed on the type and length
command.Parameters.Add(new OleDbParameter("SQLMigrationFl", OleDbType.VarChar, 10)).Value = "I";
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
//clear the In Progress flag
const string UpdateQuery = "UPDATE NoteTable SET SQLMigrationFl = ? WHERE NoteTable.NoteID = ?";
using(var commandUpdate = new OleDbCommand(UpdateQuery, connection))
{
commandUpdate.Parameters.Add(new OleDbParameter("SQLMigrationFl", OleDbType.VarChar, 10)).Value = DBNull.Value;
commandUpdate.Parameters.Add(new OleDbParameter("NoteId", OleDbType.Int)).Value = reader[19];
commandUpdate.ExecuteNonQuery();
}
}
}
}

Fetch Mysql data with Coma after each fetch C# Mysql

I am trying to fetch the data from mysql data base Column where say i have multiple rows data for specific column and i need to include coma after each row fetch.
before it was giving the data when i tried to add coma
Current Output after adding code Response.Write(name.Split(','));
System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]System.String[]
My DB
Phone_Number School_id
1 SC1
2 SC1
3 SC1
4 SC1
Expected Output
1,2,3,4
My Fetch Query
string constr = ConfigurationManager.ConnectionStrings["Logging"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
using (MySqlCommand MySqlCommand = new MySqlCommand("SELECT Phone_Number FROM Login where SchoolId='" + SessionManager.SchoolId + "'", con))
{
MySqlCommand.CommandType = CommandType.Text;
con.Open();
MySqlDataReader MySqlDataReader = MySqlCommand.ExecuteReader();
while (MySqlDataReader.Read())
{
string name = MySqlDataReader["FatherFullName"].ToString();
Response.Write(name.Split(','));
}
con.Close();
}
}
String.Split method splits your string with special character or string. This is not what you want.
You can add your Phone_Number values to a List<string> and you can use string.Join(string, IEnumerable<String>) method to generate comma separated values.
var list = new List<string>();
while(MySqlDataReader.Read())
{
string name = MySqlDataReader["FatherFullName"].ToString();
if(!string.IsNullOrEmpty(name))
{
list.Add(name);
}
}
Response.Write(string.Join(",", list)); // 1,2,3,4
You should always use parameterized queries by the way. This kind of string concatenations are open for SQL Injection attacks.
Two things more;
Default value CommandType is Text. You don't need to assign it explicitly.
You don't need to close your connection with con.Close(). Since you used using statement, it does that automatically.
String.Split is used to
split a string into substrings based on the strings in an array
You can use String.Join like this:-
//define a list of string
List<string> phoneNumbers = new List<string>();
while (MySqlDataReader.Read())
{
//add all the phone numbers to the list
string phoneNum = MySqlDataReader["FatherFullName"].ToString();
if(!String.IsNullOrEmpty(phoneNum))
phoneNumbers.Add(phoneNum);
}
//finally use Join method to get expected result
Response.Write(String.Join(",",phoneNumbers));
Also, please note your query is open for SQL Injection attack and you should consider using paramaterized query instead.
Next to the split/join, you can also get the result from MySQL directly:
SELECT GROUP_CONCAT(Phone_Number) FROM Login WHERE SchoolId = 'id';
This returns 1 single row with all phone numbers, seperated by comma's.

select query does not work with parameters using Parameters.AddWithValue

The following query in C# doesn't work, but I can't see the problem:
string Getquery = "select * from user_tbl where emp_id=#emp_id and birthdate=#birthdate";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
Reader.HasRows returns no result but when I query it to my database I got data.
I'll assume your code is actually not quite as presented, given that it wouldn't currently compile - you're using cmdR before you declare it.
First, you're trying to use named parameters, and according to the documentation of OdbcCommand.Parameters, that isn't supported:
When CommandType is set to Text, the .NET Framework Data Provider for ODBC does not support passing named parameters to an SQL statement or to a stored procedure called by an OdbcCommand. In either of these cases, use the question mark (?) placeholder.
Additionally, I would personally avoid using AddWithValue anyway - I would use something like:
string sql = "select * from user_tbl where emp_id = ? and birthdate = ?";
using (var connection = new OdbcConnection(...))
{
connection.Open();
using (var command = new OdbcCommand(sql, connection))
{
command.Parameters.Add("#emp_id", OdbcType.Int).Value = userValidate.EmployeeId;
command.Parameters.Add("#birthdate", OdbcType.Date).Value = userValidate.BirthDate;
using (var reader = command.ExecuteReader())
{
// Use the reader here
}
}
}
This example uses names following .NET naming conventions, and demonstrates properly disposing of resources... as well as fixing the parameter issue.
I do think it's slightly unfortunate that you have to provide a name for the parameter when adding it to the command even though you can't use it in the query, but such is life.
Use like this:
string Getquery = "select * from user_tbl where emp_id=? and birthdate=?";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
while(Reader.Read())
{
//Do something;
}
I know this thread is old, but I wanted to share my solution for anyone else coming up on this.
I was having issues with the typical method that Jon posted. I have used it before, but for some reason with this new string I had it was not wanting to actually place the parameter correctly and was causing the reader to not work.
I ended up doing something like this instead, since in the end we are just replacing parts of a string.
string sql = "select * from user_tbl where emp_id = "+ var1 +" and birthdate = "+
var2""
OdbcCommand command = new OdbcCommand(sql);
This was easier for me to get to work. Be warned though, I am not sure if it has any specific drawbacks when compare to using the command parameter method.

How to prevent a SQL Injection escaping strings

I have some queries (to an acccess database) like this :
string comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL='" + user + "' AND PASSWORD_AZIENDA='" + password + "'";
and I'd like to "escape" user and password, preventing an injection.
How can I do it with C# and .NET 3.5? I'm searching somethings like mysql_escape_string on PHP...
You need to use parameters. Well dont have to but would be preferable.
SqlParameter[] myparm = new SqlParameter[2];
myparm[0] = new SqlParameter("#User",user);
myparm[1] = new SqlParameter("#Pass",password);
string comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL=#User AND PASSWORD_AZIENDA=#Pass";
Don't escape the strings to start with - use a parameterized query. Benefits of this over escaping:
The code is easier to read
You don't have to rely on getting the escaping correct
It's possible that there are performance improvements (DB-specific etc)
It separates "code" (the SQL) from the data, which is just good sense logically
It means you don't need to worry about data formats for things like numbers and dates/times.
The docs for SqlCommand.Parameters give a good, complete example.
You should use the SQL paramters to prevent SQL Injection
look at the code
//
// The name we are trying to match.
//
string dogName = "Fido";
//
// Use preset string for connection and open it.
//
string connectionString = ConsoleApplication716.Properties.Settings.Default.ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
//
// Description of SQL command:
// 1. It selects all cells from rows matching the name.
// 2. It uses LIKE operator because Name is a Text field.
// 3. #Name must be added as a new SqlParameter.
//
using (SqlCommand command = new SqlCommand("SELECT * FROM Dogs1 WHERE Name LIKE #Name", connection))
{
//
// Add new SqlParameter to the command.
//
command.Parameters.Add(new SqlParameter("Name", dogName));
//
// Read in the SELECT results.
//
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int weight = reader.GetInt32(0);
string name = reader.GetString(1);
string breed = reader.GetString(2);
Console.WriteLine("Weight = {0}, Name = {1}, Breed = {2}", weight, name, breed);
}
}
}
Yes, you can avoid injection by using Named Parameters
Use parameters instead of escaping strings:
var comando = "SELECT * FROM ANAGRAFICA WHERE E_MAIL=#user AND PASSWORD_AZIENDA=#password";
Then assign values to those parameters before you execute the SqlCommand.
You can check the below link to know how to prevent SQL injection in ASP.Net. I would prefer to use
Using parametrized queries or Stored Procedures.
Validating special characters like '(very dangerous)
http://dotnet.dzone.com/news/aspnet-preventing-sql-injectio
If you can convert these to Named Parameters, I think you would be better served.
#Jethro
You could also write it like this:
SqlParameter[] sqlParams = new SqlParameter[] {
new SqlParameter("#Name", contact.name),
new SqlParameter("#Number", contact.number),
new SqlParameter("#PhotoPath", contact.photoPath),
new SqlParameter("#ID", contact.id)
};
Follow the steps below and resolve the SQL INJECTION problem:
OracleParameter[] tmpParans = new OracleParameter[1];
tmpParans[0] = new Oracle.DataAccess.Client.OracleParameter("#User", txtUser.Text);
string tmpQuery = "SELECT COD_USER, PASS FROM TB_USERS WHERE COD_USER = #User";
OracleCommand tmpComand = new OracleCommand(tmpQuery, yourConnection);
tmpComand.Parameters.AddRange(tmpParans);
OracleDataReader tmpResult = tmpComand.ExecuteReader(CommandBehavior.SingleRow);

How do get a simple string from a database

The following code does not work. There is only 1 row in this table. How do I just get the statement that the sql would return?:
SqlConnection conn = new SqlConnection(connectionStringArg);
SqlCommand command = new SqlCommand("select applicationname from tbl_settings");
command.Connection = conn;
conn.Open();
string simpleValue = command.ExecuteReader()[0].ToString();
conn.Close();
return simpleValue;
Okay any help on how to achieve this relatively simple task would be great.
Since there's only a single value returned, you could do this:
string value = (string)command.ExecuteScalar();
If you need more than the first column from the first row returned, you'll need to use ExecuteReader(). See driis' answer.
The DataReader can't be indexed like that. You need something like:
using(conn)
using(var reader = command.ExecuteReader())
{
reader.Read();
string simpleValue = reader.GetString(0);
}
The general idea is to advance the reader for each record (using Read), then you can read values from the row. As pointed out in another answer, if you know there is only one single value in the result set, command.ExecuteScalar() gives you just that.
You have to call the Read method on a DataReader returned from ExecuteReader to get to the first row. Something like this:
SqlDataReader rdr = command.ExecuteReader();
if (rdr.Read())
...
You'd do e.g.
using(SqlConnection conn = new SqlConnection(connectionStringArg))
using(SqlCommand command = new SqlCommand("select applicationname from tbl_settings")) {
command.Connection = conn;
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if(reader.read()) {
return reader.GetString(0);
}
return null;
}
For a single string you want to use ExecuteScalar to get a single value and then attempt to cast it to string.
From doc: Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
Example:
string result = command.ExecuteScalar() as string;
For small stuff I often find LINQ2SQL the easiest to set up and use. This tutorial should get you up and running in a few minutes. For bigger projects LINQ2SQL is considered obsolete, see this question for discussion and alternatives.
Check DataReader.Read() you need to call it.
string applicationname = "";
using(var reader = command.ExecuteReader())
{
reader.Read();
applicationname = reader.GetString(reader.GetOrdinal("applicationname"));
}
var query_result = com.ExecuteScalar() as string;
if (!string.IsNullOrEmpty(query_result )) // do your thing here...

Categories

Resources