I want to make a sql query in C# that finds all rows with a key that is specified in a list. Can I do this with one query? I suppose that is much more efficent than my solution which finds one item at the time inside a for loop, se below:
foreach (int i in list)
{
string Q = "... where pk = " + i.ToString();
using (SqlCommand CM = new SqlCommand(Q, C))
{
using (SqlDataReader R = CM.ExecuteReader())
{
while (R.Read())
{
...
}
}
}
}
list contains different in values.
Thanks in advance!
Replace
string Q = "... where pk = " + i.ToString();
with
string Q = "... where pk IN ('" + string.Join("','", list)+"')";
then you can remove the loop. The result should look like ... where pk IN ('1','2','3')
You can use the IN keyword and pass your list by converting it to a comma seperated string in your query.
Something like
string Q = "select * from tablename where pk IN " + (comma seperated list here);
Related
I like to do something like this in code:
string[] columns = new string[]{"col1, col2"};
var query = SELECT(columns).FROM("ANY_TABLE");
string strQuery = query.ToString();
and strQuery should contain now a sql query as string like:
SELECT col1, col2 FROM ANY_TABLE
I tried already to find something like this but I don't know any labels which I could use.
I know there is LINQ but I think it only works with EF and not sure whether it can output such a string.
THANKS
A simple approach could be:
string[] columns = new string[] { "col1", "col2" };
var query = #"SELECT ";
foreach(var column in columns)
{
query += " " + column + ", ";
}
query = query.Substring(0, query.Length-2);
query += #" FROM ANY_TABLE ";
string strQuery = query.ToString();
I am in the process of fixing some of our bad sql queries that are vulnerable to sql injection. Most are straight queries with no inputs, but our search field takes search terms that are not parameterised. A snippet is below:
using (var db = ORMLite.Open())
{
StringBuilder sb = new StringBuilder();
sb.Append("select * from column1, column2");
if (terms.Count() > 0)
{
sb.Append("where (column1 like '%#term0%' or " + column2 + " like '%#term0%') ");
if (terms.Count() > 1)
{
for (int i = 1; i < terms.Count(); i++)
{
sb.Append("and (column1 like '%#term" + i + "%' or " + column2 + " like '%#term" + i + "%') ");
}
}
}
List<POCO> testQuery = db.Select<POCO>(sb.ToString());
}
The #term components are where I intend to use parameters (they used to be of the form '" + term[i] + '", but any term with malicious code would just be inserted. When I move to my select statement, I would like to add the parameters. This is normally done as so:
List testQuery = db.Select(sb.ToString(), new { term0 = "t", term1 = "te", term2 = "ter" });
However I can have any number of terms (term.count() is the number of terms). How can I pass in an anonymous object with any number of terms? Or is there a better method?
I'm looking for almost the same thing in Postgresql. Based on this SO question
the answer looks like "you have to perform multiple queries."
I can get the unique row IDs from my table given the partial parameterized
query, and then directly paste those unique IDs back into the query -- since those
row IDs will be safe.
Here's an example of what I mean, but the c# is probably wrong (sorry):
string query = "SELECT unique_id FROM table WHERE (column1 LIKE '%#term%' OR column2 LIKE '%#term%')";
string safeIDs;
List uniqueRowIDs = db.Select(query, new {term = term[0]});
for (int i = 1; i < terms.Count(); i++) {
// Loop to narrow down the rows by adding the additional conditions.
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
uniqueRowIDs = db.Select(
query + string.Format(" AND unique_id IN ({0})", safeIDs),
new {term = term[i]});
}
// And finally make the last query for the chosen rows:
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
List testQuery = db.Select(string.Format("SELECT * FROM table WHERE unique_id IN ({0});", safeIDs));
Another option for your case specifically could be to just get all of the values that
are like term0 using a parameterized query and then, within the c# program, compare
all of the results against the remaining terms the user entered.
guys i have an SQL statement returning more than 1 value.
I am trying to use the StreamReader to get the values into an array as below
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME=' " + table + "' and CONSTRAINT_NAME like 'PK_%'";
SqlConnection conn2 = new SqlConnection(cnstr.connectionString(cmbDatabase.Text));
SqlCommand cmd_server2 = new SqlCommand(sql);
cmd_server2.CommandType = CommandType.Text;
cmd_server2.Connection = conn2;
conn2.Open();
//reader_sql = new StreamReader();
SqlDataReader reader_sql = null;
string[] colName = new string[200];
reader_sql = cmd_server2.ExecuteReader();
while (reader_sql.Read());
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
It is not working, what am I doing wrong guys ?
You've got a stray ; turning your while into a tight loop, so instead try:
while (reader_sql.Read())
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
You get the exception because
while (reader_sql.Read());
should be
while (reader_sql.Read())
{
for (int rr = 0; rr < 20; rr++)
{
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
}
Perhaps you should remove the semicolon at the end of Read
while (reader_sql.Read())
{
for (int rr = 0; rr < 20; rr++)
colName[rr] = reader_sql["COLUMN_NAME"].ToString();
}
However, if your intention is to retrieve the columns belonging to the primary key, your code is wrong because you add 20 times the same primary key column, then repeat the same for the remaining columns ending with an array of 20 strings all equals to the last column in the primary key set. I think you should change your code to use a List(Of String) instead of a fixed length array and let the reader loop correctly on the primary key columns retrieved
List<string> pks = new List<string>();
while (reader_sql.Read())
{
pks.Add(reader_sql["COLUMN_NAME"].ToString());
}
EDIT: I have just noticed that your query contains a space before the table name. The string concatenation then produces an invalid table name, the query is syntactically right but doesn't return any data
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE " +
"where TABLE_NAME='" + table + "' and CONSTRAINT_NAME like 'PK_%'";
^ space removed here
And while you are at it, remove the string concatenation and use a parameterized query.....
string sql = "select distinct COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE " +
"where TABLE_NAME=#tName and CONSTRAINT_NAME like 'PK_%'";
SqlCommand cmd_server2 = new SqlCommand(sql, connection);
connection.Open();
cmd_server2.Parameters.AddWithValue("#tName", table);
If I have a list of Strings, ie. List<String>, how can I generate a SQL statement such as:
SELECT Column1 FROM Table1 WHERE Column1 IN ('String1','String2','String3')
where 'String1','String2','String3' are the contents of List<String>?
No LINQ etc. as I am using VS2005.
Take a look on following version
[Test]
public void Test()
{
var list = new List<string> {"String1", "String2", "String3"};
string values = ArrayToString(list);
string sql = string.Format("SELECT Column1 FROM Table1 WHERE Column1 IN ( {0} )", values);
}
private static string ArrayToString(IEnumerable<string> array)
{
var result = new StringBuilder();
foreach (string element in array)
{
if (result.Length > 0)
{
result.Append(", ");
}
result.Append("'");
result.Append(element);
result.Append("'");
}
return result.ToString();
}
result statement SELECT Column1 FROM Table1 WHERE Column1 IN ( 'String1', 'String2', 'String3' )
List<string> lst=new List<string>();lst.Add("Hello");lst.Add("Hello World");
string s="";
foreach(string l in lst)s+="\""+l+"\"";
s=Regex.Replace(s,"\"\"","\",\"");
string output="SELECT Column1 FROM Table1 WHERE Column1 ("+s+")";
try :
List<String> strlist = new List<string>();
strlist.Add("st1");
strlist.Add("st2");
strlist.Add("st3");
string query = "SELECT Column1 FROM Table1 WHERE Column1 IN (";
for (int i = 0; i < strlist.Count; i++)
{
query += "\'" + strlist[i] + "\'" + (i == strlist.Count - 1 ? "" : ",");
}
query += ")";
List<string> items = new List<string>();
items.Add("string1");
items.Add("string2");
items.Add("string3");
string AllItems = "";
foreach (string item in items)
{
AllItems += string.Format("\"{0}\",",item);
}
AllItems = AllItems.TrimEnd(',');
string YourSQLQuery = string.Format("SELECT Column1 FROM Table1 WHERE Column1 IN ({0})", AllItems);
MessageBox.Show(YourSQLQuery);
Don't for get to guard against SQL Injection.
string sql_list = "";
foreach (string s in lst)
sql_list+=string.Format("{0},",s.Replace("'","''"));
sql_list = string.Format("({0})",sql_list.substring(0,sql_list.length-2));
that might help some, and use string builder, or not.
Please don't use the other answers that have been submitted so far. They contain SQL injection for no obvious reason.
List<String> strlist = new List<string>();
strlist.Add("st1");
strlist.Add("st2");
strlist.Add("st3");
var dynamicPart = string.Join(", ",
Enumerable.Range(0, strlist.Count).Select(i => "#" + i).ToArray());
for(i = 0 to strlist.Count)
{ /* add parameter to SqlCommand here with name ("#" + i) */ }
string query = "SELECT Column1 FROM Table1 WHERE Column1 IN (" +
dynamicPart + ")";
Use parameters instead of literals for multiple reasons (research them!).
And instead of a clumsy concatenation loop use string.Join which does all of that for us.
To properly handle sql injection, a better answer may be to make the query of the form...
select results.* from (
select pk from table where column=value1 union
select pk from table where column=value2 union
select pk from table where column=value3 union
select pk from table where column=value4 union
select pk from table where column=value5
) filtered join table as results on filtered.pk = results.pk
and then make it more c# friendly
string items_filter = "";
int item_index=0;
OracleParameterCollection parameters = new OracleParameterCollection(); // Not sure what class to use here exactly, but just collect a bunch of stored procedure parameters
foreach (string item in list_of_items) {
string item_name = string.Format("i_item{0}",item_index);
string item_sql = string.Format("select pk from table where column=:{0} union",item_name);
parameters.Add(new Parameter("item_name",item));
item_index+=1;
}
if (items_filter.IsNullOrEmpty())
return;
string sql = String.Format("select results.* from ({0}) filtered join table as results on filtered.pk = results.pk",items_filter);
OracleCommand c = new OracleCommand();
c.command = sql;
c.parameters = parameters;
c.execute();
More or less.
Since you said its an internal operation and hence there is no need to be worried about SQL Injection, then you can achieve what you want by this.
string str = "";
foreach(string s in list)
str += "'" + s.Replace("'", "''") + "',";
str = str.SubString(0, str.Length - 1);
str = "SELECT Column1 FROM Table1 WHERE Column1 IN (" + str + ")";
//str will have your command ready.
I have tested it. It works perfectly.
// Assume your list (List<string>) is named "myList"
// Please put the next line in an external string resource...
string selectStatement = "SELECT Column1 FROM Table1 WHERE Column1 IN ({0})";
StringBuilder stringBuilder = new StringBuilder("(");
foreach(string colName in myList)
stringBuilder.Append(String.Format("'{0}',", colName));
stringBuilder.Append(")");
return String.Format(selectStatement, stringBuilder.ToString().Replace(",)", ")");
hello i build a webservice in visual studio 2010. i get some id's which are saved in a string looks like this:
string room_ids="5,11,99,42";
they are separated by comma. i created a foreach loop to split the ids and from the comma and use them in my sql query until the ids are finished. but it doesn't work. i get an error it says:
Error converting data type nvarchar to numeric
here is my code, thanks in advance for your help!
internal static List<RAUM> Raum(string RAUMKLASSE_ID, string STADT_ID, string GEBAEUDE_ID, string REGION_ID)
{
List<RAUM> strasseObject = new List<RAUM>();
string[] allegebaude = GEBAEUDE_ID.Split(new char[] { ',' });
foreach (string gebaudeid in allegebaude)
{
Trace.WriteLine("SIND JETZT DRINNE");
Trace.WriteLine(gebaudeid);
using (SqlConnection con = new SqlConnection(#"Data Source=Localhost\SQLEXPRESS;Initial Catalog=BOOK-IT-V2;Integrated Security=true;"))
using (SqlCommand cmd = new SqlCommand(#"SELECT r.BEZEICHNUNG AS BEZEICHNUNG, r.ID AS ID FROM RAUM r WHERE RAUMKLASSE_ID = ISNULL(#Raumklasse_ID, RAUMKLASSE_ID) AND STADT_ID = ISNULL(#Stadt_ID, STADT_ID) AND GEBAEUDE_ID = ISNULL(#gebaudeid,GEBAEUDE_ID ) AND REGION_ID = ISNULL(#Region_ID, REGION_ID)", con))
{
con.Open();
if (!StringExtensions.IsNullOrWhiteSpace(RAUMKLASSE_ID))
cmd.Parameters.AddWithValue("#Raumklasse_ID", RAUMKLASSE_ID);
else
cmd.Parameters.AddWithValue("#Raumklasse_ID", DBNull.Value);
if (!StringExtensions.IsNullOrWhiteSpace(STADT_ID))
cmd.Parameters.AddWithValue("#Stadt_ID", STADT_ID);
else
cmd.Parameters.AddWithValue("#Stadt_ID", DBNull.Value);
if (!StringExtensions.IsNullOrWhiteSpace(GEBAEUDE_ID))
cmd.Parameters.AddWithValue("#gebaudeid", GEBAEUDE_ID);
else
cmd.Parameters.AddWithValue("#gebaudeid", DBNull.Value);
if (!StringExtensions.IsNullOrWhiteSpace(REGION_ID))
cmd.Parameters.AddWithValue("#Region_ID", REGION_ID);
else
cmd.Parameters.AddWithValue("#Region_ID", DBNull.Value);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
if (rdr["BEZEICHNUNG"] != DBNull.Value && rdr["ID"] != DBNull.Value)
{
strasseObject.Add(new RAUM()
{
RaumName = rdr["BEZEICHNUNG"].ToString(),
RaumID = rdr["ID"].ToString()
});
}
}
}
}
}
return strasseObject;
}
If you already have the IDs in a comma-separated string (called IDstring) then you can just do something like this:
sqlQuery = "SELECT Columns FROM table WHERE ID IN (" + IDstring + ")";
In your specific case, don't split the original string (GEBAEUDE_ID) but use it as it is:
// Don't use a foreach loop any more
string gebaudeIdSection = " AND GEBAEUDE_ID IN (" + GEBAEUDE_ID + ") ";
if (string.IsNullOrEmpty(GEBAUDE_ID)) { gebaudeIdSection = ""; } // if there are no ids, let's remove that part of the query completely.
using (SqlConnection con = new SqlConnection(#"Data Source=Localhost\SQLEXPRESS;Initial Catalog=BOOK-IT-V2;Integrated Security=true;"))
using (SqlCommand cmd = new SqlCommand(#"SELECT r.BEZEICHNUNG AS BEZEICHNUNG, r.ID AS ID FROM RAUM r WHERE RAUMKLASSE_ID = ISNULL(#Raumklasse_ID, RAUMKLASSE_ID) AND STADT_ID = ISNULL(#Stadt_ID, STADT_ID)" + gebaudeIdSection + " AND REGION_ID = ISNULL(#Region_ID, REGION_ID)", con))
{ // The rest of the code is the same as before...
First of all I think you have to correct:
cmd.Parameters.AddWithValue("#gebaudeid", GEBAEUDE_ID);
with:
cmd.Parameters.AddWithValue("#gebaudeid", gebaudeid);
Then, try to convert the ids into integers ( for example, using Convert.ToInt32(gebaudeid) ) and not to pass them as strings in AddWithValue method.
try this:
if (!StringExtensions.IsNullOrWhiteSpace(gebaudeid))
cmd.Parameters.AddWithValue("#gebaudeid", Convert.ToInt32(gebaudeid));
you should also pass the right parameter to your AddWithValue statement. You are iterating over the list of your ID's, that list being allegebaude. So you have to pass the gebaudeid parameter to your statement, instead of what you're doing now.
You can't implicitly treat a comma separated list of values; instead you'll need to create a table values function to split the list of values into a table structure and then use this structure as you would normally.
Have a look at this question: INSERT INTO TABLE from comma separated varchar-list for a sample function.