I want to select several values from database (ODBC datasource). The table is this simple:
| name | value |
+------+-------+
| abcd | 12345 |
Say I want to select values where name is name1, name2 and name3:
SELECT name, value FROM my_table WHERE name="name1" OR name="name2" OR name="name3"
Now I could generate this command:
public string MakeCommand(List<string> names) {
string command = "SELECT name, value FROM my_table WHERE ";
bool first = true;
foreach(string name in names) {
if(first)
first = false;
else
command+=" OR ";
command+="name=\""+name+"\"";
}
}
I hope it's not necessary to emphasize that this would be very bad way to access the database.
So how can I make this a parametrized ODBC command, as described here?
Well, the simplest solution is probably to concatenate a parameter to the sql for each value in your list, and then add this value as a parameter to the OdbcCommand.
using(var command = new OdbcCommand())
{
string sql = "SELECT name, value FROM my_table WHERE name IN(";
for(int i=0; i < names.Count; i++) {
sql = $"{sql} #{i},";
command.Parameters.Add($"#{i}", OdbcType.VarChar).Value = names[i];
}
command.CommandText = sql.TrimEnd(",") +");";
command.Connection = con;
// fill a data set or execute a data reader here....
}
There is no elegant solution to this problem. You can use the IN clause but, still you need to build the parameters one by one. So instead of returning a string you can return the OdbcCommand prepared with all the parameters required by your list of names. Just need to add the connection and execute (or pass also the connection and prepare everything here)
public OdbcCommand MakeCommand(List<string> names, OdbcConnection con) {
List<OdbcParameter> parameters = new List<OdbcParameter>();
List<string> placeholders = new List<string>();
foreach(string name in names) {
OdbcParameter p = new OdbcParameter("?", OdbcType.NVarChar);
p.Value = name;
parameters.Add(p);
placeholders.Add("?")
}
string command = "SELECT name, value FROM my_table WHERE name IN(";
command = command + string.Join(",", placeholders.ToArray()) + ")";
OdbcCommand cmd = new OdbcCommand();
cmd.CommandText = command;
cmd.Connection = con;
cmd.Parameters.AddRange(parameters.ToArray());
return cmd;
}
If you still have problems, then it could be something linked to the parameters DataType. NVarChar, VarChar, Text, String. Some db could react differently to this type. What database are you testing this code against?
Related
I have been working on a personal project for the company I work for to control stock levels in order to practice my c#.
I want my application to search through tblJuiceStock, find a matching FlavourID to what the user is inputting and update the stock of that record through an UPDATE SET query.
public void InsertJuiceStockWithCheck()
{
using (OleDbConnection conn = new OleDbConnection())
{
conn.ConnectionString = ConnectionString;
conn.Open();
string tblJuiceStockCheck = "SELECT FlavourID, Quantity FROM tblJuiceStock";
OleDbCommand cmdCheck = new OleDbCommand(tblJuiceStockCheck, conn);
OleDbDataAdapter daCheck = new OleDbDataAdapter(cmdCheck);
DataTable dtCheck = new DataTable();
daCheck.Fill(dtCheck);
foreach (DataRow row in dtCheck.Rows)
{
if ((int)row["FlavourID"] == fID)
{
int currentQty = (int)row["Quantity"];
int updatedQty = currentQty + qty;
string tblJuiceStockExisting = #"UPDATE tblJuiceStock
SET Quantity = #newquantity
WHERE FlavourID = #flavourID";
OleDbCommand cmdJuiceStockExisting = new OleDbCommand(tblJuiceStockExisting, conn);
cmdJuiceStockExisting.Parameters.AddWithValue("#flavourID", fID);
cmdJuiceStockExisting.Parameters.AddWithValue("#newquantity", updatedQty);
cmdJuiceStockExisting.ExecuteNonQuery();
matchFound = true;
break;
}
}
if (!matchFound)
{
string tblJuiceStockNew = "INSERT INTO tblJuiceStock (FlavourID, Quantity, MinStockPOS) VALUES (#fID, #quantity, #minstock)";
OleDbCommand cmdJuiceStockNew = new OleDbCommand(tblJuiceStockNew, conn);
cmdJuiceStockNew.Parameters.AddWithValue("#fID", fID);
cmdJuiceStockNew.Parameters.AddWithValue("#quantity", qty);
cmdJuiceStockNew.Parameters.AddWithValue("#minstock", amt);
cmdJuiceStockNew.ExecuteNonQuery();
}
}
}
Please note: this query works fine in Access when I replace parameters with the same values. Also, using breakpoints I identified that the parameters have the correct values set to them, the variables assigned to them are obtained within another method, all methods are called in the submit button event.
However, the Quantity value in TblJuiceStock remains the same.
My tblJuiceStock table
After some time of messing about the answer was simple.
OLEDB does work with named parameters but you have to declare them, if you don't declare them they use the parameters positioning to match them up.
My problem was that in my query string I had #newquantity first and #flavourID second, whereas when adding my parameters I added #flavourID first and #newquantity second.
I'm facing problem in creating table programtically in asp.net c#. I'm working on sql project. I have a gridview and a button,When I click on button then I want that all gridview columns values e.g column_name,data type,allowNull,PrimaryKey etc.
All values inserted inside the "Create Table QUERY" in one iteration and table will be created, But I have a problem. I'm using for loop when loop first time execute then only one row iterate and table created in SQL only one column(Just first row) and when 2nd iteration execute then table name will be same ,so there is a issue.
Kindly tell me how can I resolve this issue.All values successfully inserted into the table but problem is in creating table. Table is created but 'ONLY LAST ' row table is created,Table contain only one row.How can i resolve this issue.
How can i do this?
Here is my "button" code aspx.cs`
public void insert(object sender, EventArgs e)
{
SqlConnection cnn = new SqlConnection("Data Source=HAMEED_KHAN\\SQLEXPRESS;Initial Catalog=db_compiler;Integrated Security=True");
string d=Session["value"].ToString();
SqlCommand cmd2=new SqlCommand("SELECT Database_id FROM Create_db WHERE Database_Name='"+d+"'",cnn);
cnn.Open();
string dbid = cmd2.ExecuteScalar().ToString();
cnn.Close();
int D_ID = Int32.Parse(dbid);
string str = "";
string type = "";
for (int i = 0; i < GridView2.Rows.Count; i++)
{
string tblname = "abc";
str=GridView2.Rows[i].Cells[1].Text.ToString();
type=GridView2.Rows[i].Cells[2].Text.ToString();
string Name = GridView2.Rows[i].Cells[1].Text.ToString();
string Type = GridView2.Rows[i].Cells[2].Text.ToString();
CheckBox allow=GridView2.Rows[i].Cells[3].Controls[0]as CheckBox;
CheckBox primary = GridView2.Rows[i].Cells[4].Controls[0] as CheckBox;
string s = Session["UID"].ToString();
int id = Int32.Parse(s);
string date = DateTime.Now.ToString();
string A = (allow.Checked == true ? "NULL" : "NOT NULL");
string P = (primary.Checked == true ? "PRIMARY KEY" : "");
// string query="USE "+d+" CREATE TABLE ABCD ("+Name+" "+Type+" "+A+")";
// SqlCommand cmd3 = new SqlCommand(query, cnn);
SqlCommand cmd = new SqlCommand("insertTbl", cnn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Name", tblname);
cmd.Parameters.AddWithValue("#col_name", Name);
cmd.Parameters.AddWithValue("#dtype",Type);
cmd.Parameters.AddWithValue("#dbId", D_ID);
cmd.Parameters.AddWithValue("#allow",(allow.Checked==true ? "true" : "false"));
cmd.Parameters.AddWithValue("#primary", (primary.Checked == true ? "true" : "false"));
cmd.Parameters.AddWithValue("#user", id);
cmd.Parameters.AddWithValue("#date", date);
SqlDataAdapter ad = new SqlDataAdapter(cmd);
cnn.Open();
cmd.ExecuteNonQuery();
// cmd3.ExecuteNonQuery();
cnn.Close();
}
string str1=str;
string str2=type;
//string AA="ALLOW NULL";
// string queryy =string.Format(#"USE {"+d+"}; IF (NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE NAME = 'ABCDE'))CREATE TABLE ABCDE ({"+str1+"} {"+type+"} {"+AA+"})");
string queryy="USE "+d+" If not exists (select name from sysobjects where name = 'Customers') CREATE TABLE Customers("+str1+" "+type+")";
SqlCommand cmd4 = new SqlCommand(queryy, cnn);
cnn.Open();
cmd4.ExecuteNonQuery();
cnn.Close();
}
You should make sure to avoid recreating the same table by using a rerunnable script. For CREATE TABLE, checking its existence is done this way:
string query = String.Format(#"
USE {0};
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = '{1}'))
CREATE TABLE {1} ({2} {3} {4})", d, "ABCD", Name, Type, A);
Notice that I have used String.Format to increase readability and avoid string concatenation (strings are immutable, so many instances are created when using + operator).
However, consider moving your CREATE TABLE outside of for loop, if your intention is to create once and insert multiple times. Anyway, existence check should be performed.
From C# 6.0, you can use both verbatim and interpolation (actually, string interpolation was introduced in 6.0). Something like this:
string query = $#"
USE {d};
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'ABCD'))
CREATE TABLE ABCD ({Name} {Type} {A})";
My sample code is as follows, I am getting following error;
Data Type Mismatch error in criteria expression.
Details => ScannerAlarmLimits is my table from .mdb database.
string jointS = dsetChamberS1.Tables[0].Rows[tot][0].ToString();
int select1S = Convert.ToInt32(jointS);
string sqlQuery1S = "SELECT TMin,TMax,HMin,HMax from ScannerAlarmLimits WHERE ScannerID='" +select1S+ "'";
OleDbCommand cmd1S = new OleDbCommand(sqlQuery1S, conn);
OleDbDataAdapter adapter1S = new OleDbDataAdapter(cmd1S);
adapter1S.Fill(dsetTempS, "ScannerAlarmLimits");
I just added single quote in the condition of where clause, now its working.
var query = "SELECT * from checkinout where read <> '1'";
If your ScannerID column is integer, then you should not use single quotes with it. Single quotes are for characters. Like;
WHERE ScannerID = " + select1S;
But as a better way, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks. Aka bobby-tables.
And use using statement to dispose your connections, commands and adapters.
string jointS = dsetChamberS1.Tables[0].Rows[tot][0].ToString();
int select1S = Convert.ToInt32(jointS);
using(var conn = new OleDbConnection(conString))
using(var cmd1S = conn.CreateCommand())
{
cmd1S.CommandText = "SELECT TMin,TMax,HMin,HMax from ScannerAlarmLimits WHERE ScannerID = #id";
cmd1S.Parameters.AddWithValue("#id", OleDbType.Integer).Value = select1S;
using(var adapter1S = new OleDbDataAdapter(cmd1S))
{
adapter1S.Fill(dsetTempS, "ScannerAlarmLimits");
}
}
//the #Question column name needs to change according to the checkbox. For example Checkbox1 - Question1
SqlConnection con = new SqlConnection(...);
String sql = "UPDATE INQUIRY2 set #Question = #str WHERE email = #email AND base = #base;";
SqlCommand cmd = new SqlCommand(sql, con);
con.Open();
//checkbox2 - question 2
//if (CheckBox3.Checked == true)
//{
// str = str + CheckBox3 + 'x';
//}
DataTable theDataTable = null;
// Verify that dt is actually in session before trying to get it
if(Session["dt"] != null)
{
theDataTable = Session["dt"] as DataTable;
}
//Verify that the data table is not null
if(theDataTable != null)
{
email = theDataTable.Rows[0]["email"].ToString();
base1 = theDataTable.Rows[0]["base"].ToString();
}
//checkbox1 - question 1
if (CheckBox9.Checked == true)
{
str = str + CheckBox9.Text + 'x';
strQuestOne = theDataTable.Columns["Question1"].ToString();
}
cmd.Parameters.AddWithValue("#email", email);
cmd.Parameters.AddWithValue("#str", str);
cmd.Parameters.AddWithValue("#Question", strQuestOne);
cmd.Parameters.AddWithValue("#base", base1);
cmd.ExecuteNonQuery();
con.Close();
You are using a parameter for a column name. Database objects (columns names, tables, stored procedures or any other objects) cannot be passed as parameters. Only actual values for columns or variables can be parameters. You need to build your SQL statement dynamically in this case:
String sql ="UPDATE INQUIRY2 set " + strQuestOne + "= #str WHERE email = ...
But now you should be carefull because you code is at risk of SQL injection attack.
Your SQL uses #param type in the string.
If you are looking to execute a stored procedure of some sort to negate most sql injection attacks on a website you might want to consider calling a stored procedure and adding SqlCommand.Parameter to the SqlCommand.Parameters Collection.
Otherwise if you want to just execute the sql you should do
string sql = String.Format("UPDATE TABLE set COLUMN = {0} where OTHERCOLUMN = {1}", varValue, varWhere);
This question already has answers here:
Pass Array Parameter in SqlCommand
(11 answers)
Closed 6 years ago.
I seem to be confused on how to perform an In statement with a SqlParameter. So far I have the following code:
cmd.CommandText = "Select dscr from system_settings where setting in #settings";
cmd.Connection = conn;
cmd.Parameters.Add(new SqlParameter("#settings", settingList));
reader = cmd.ExecuteReader();
settingsList is a List<string>. When cmd.ExecuteReader() is called, I get an ArgumentException due to not being able to map a List<string> to "a known provider type".
How do I (safely) perform an In query with SqlCommands?
You could try something like this:
string sql = "SELECT dscr FROM system_settings WHERE setting IN ({0})";
string[] paramArray = settingList.Select((x, i) => "#settings" + i).ToArray();
cmd.CommandText = string.Format(sql, string.Join(",", paramArray));
for (int i = 0; i < settingList.Count; ++i)
{
cmd.Parameters.Add(new SqlParameter("#settings" + i, settingList[i]));
}
You appear to be trying to pass a multi valued parameter, that SQL syntax isn't going to do what you expect. You may want to pass a table value parameter.
Read this: http://www.sommarskog.se/arrays-in-sql.html#iter-list-of-strings
specifically: http://www.sommarskog.se/arrays-in-sql-2008.html#ListSqlDataRecord
private static void datatable_example() {
string [] custids = {"ALFKI", "BONAP", "CACTU", "FRANK"};
DataTable custid_list = new DataTable();
custid_list.Columns.Add("custid", typeof(String));
foreach (string custid in custids) {
DataRow dr = custid_list.NewRow();
dr["custid"] = custid;
custid_list.Rows.Add(dr);
}
using(SqlConnection cn = setup_connection()) {
using(SqlCommand cmd = cn.CreateCommand()) {
cmd.CommandText =
#"SELECT C.CustomerID, C.CompanyName
FROM Northwind.dbo.Customers C
WHERE C.CustomerID IN (SELECT id.custid FROM #custids id)";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#custids", SqlDbType.Structured);
cmd.Parameters["#custids"].Direction = ParameterDirection.Input;
cmd.Parameters["#custids"].TypeName = "custid_list_tbltype";
cmd.Parameters["#custids"].Value = custid_list;
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
using (DataSet ds = new DataSet()) {
da.Fill(ds);
PrintDataSet(ds);
}
}
}
}
If you're using Sql Server 2008 or later, you can make use of table valued parameters - this allows you to pass in a table of values as a parameter. From .net you define a "structured" type SqlParameter and set the value to something that implements IEnumerable.
See the full MSDN reference with examples here: http://msdn.microsoft.com/en-us/library/bb675163.aspx
I ever use my own function to create Parameters like this:
public void SomeDataFunction() {
ArrayList params = GetParameters(someEntity);
CommandObject.Parameters.AddRange(parameters.ToArray());
}
public static ArrayList GetParameters(ISomeEntity entity) {
ArrayList result = new ArrayList {
OleDbUtility.NewDbParam("#Param1", OleDbType.Integer, , entity.Parameter1),
OleDbUtility.NewDbParam("#Param2", OleDbType.VarChar, 9, entity.Parameter2),
}
}
public static OleDbParameter NewDbParam(string parameterName, OleDbType dataType,
int size, object value) {
OleDbParameter result = new OleDbParameter(parameterName, dataType, size, string.Empty);
result.Value = value;
return result;
}
Use XML, it's plenty fast for this scenario. You would turn your list into XML and simply pass a string:
CREATE TABLE #myTempTable
( Letter VARCHAR(20) )
INSERT INTO #myTempTable (Letter) VALUES ('A'), ('B')
Declare #xml XML = '<a>A</a><a>B</a><a>C</a>'
Select * from #myTempTable
Where Letter in
(Select p.value('.', 'VARCHAR(40)') AS [Letter] from #xml.nodes('//a') as t(p))
DROP TABLE #myTempTable
I usually pass in the list as a comma separated string, and then use a table valued function to 'split' the string into a table that I can then use to join with in another query.
DECLARE #Settings TABLE (Sid INT)
INSERT INTO #Settings(Sid)
SELECT CAST(Items AS INT) FROM dbo.Split(#SettingsParameter, ',')
Unless of course you are using SQL Server 2008, then I would use the table valued parameters.