I have an array of objects. Each object contains around 20 members. I need to loop through the array and insert the data from the object into my database. Is there a way of doing this that does not require me to put an INSERT statement within the body of my loop? I am using C# and SQL Server.
for(int i =0; i < arr.length; i++)
{
strSQL = "INSERT INTO myTable (field1...field20) VALUES (" + arr[i].field1 + "..." + arr[i].field20)
sqlCmd.execute(strSQL,sqlConn)
}
Why don't you use linq ?
Yes there is way.
First, +1 for lollancf37's answer.
And second, you can use StringBuilder. Build your query parameter with 1 query using StringBuilder, and execute 1 time
Enjoy
SqlCommand sqlcmd = null;
SqlParameter pField1 = new SqlParameter("#Field1", System.Data.SqlDbType.VarChar, 255);
...
SqlParameter pField20 = new SqlParameter("#Field20", System.Data.SqlDbType.VarChar, 255);
try{
sqlcmd = new SqlCommand("INSERT INTO myTable (field1...field20) VALUES (#Field1,...,#Field20)",sqlConn)
sqlcmd.Parameters.Add(pField1);
...
sqlcmd.Parameters.Add(pField20);
for(int i =0; i < arr.length; i++)
{
pField1.value = arr[i].field1;
...
pField20.value = arr[i].field20;
sqlCmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
LogError(ex.message)
}
finally
{
if (sqlconn != null && sqlconn.State != System.Data.ConnectionState.Closed)
sqlconn.Close();
if (sqlcmd != null)
sqlcmd.Dispose();
}
For simple commands, you could use a format string, something like this:
//assuming that the first param is a number, the second one a string etc...
string insertFormat = #"Insert into myTable((field1...field20)
VALUES ({0},'{1}',..,19})"
for(int i =0; i < arr.length; i++)
{
//assuming that arr[i] is string[]
strSQL = string.Format(insertFormat, arr[i]);
sqlCmd.execute(strSQL,sqlConn)
}
You must be aware, however, that you are leaving yourself wide open for SQL Injection (mandatory xkcd). In order to avoid that, and other issues, you could take a look at "advanced| solutions (using a stored procedure, using reflection to map the fields, using some ORM tool, using Linq-to-sql, etc)
strSQL = "INSERT INTO MYTABLE (COL01, COL01...COL42)";
for(int i = 0; i < arr.length; i++)
{
strSQL += "SELECT (" +arr[0] +", " +arr[1] +"..." +arr[42] +")";
if(i < arr.length - 1) {
strSQL += " UNION ALL ";
}
}
sqlCommand.Execute(strSQL, conn); // I forget how this bit goes, I am not a C# programmer by trade...
I find that inserting more than 500 records at a time like this makes the database run pretty slow.
Parameterised stored procedures.
Write the insert statement down in the db as a stored procedure, with parameters.
Create a method to populate the stored procedure, passing in the object as the parameter.
Call the method in the loop, populating an passing in an object instance each time.
I had a similar problem at one point, the most effective way I found to deal with it was create a 2-dimensional array for your data, and store the column name for each line of data in the array in addition to storing the data itself.
From there, it's pretty trivial to loop through it and use a StringBuilder to assemble the query. Additionally, instead of simply throwing the insert values on, put a parameter name on them (I just used the column name). Then, in the same loop, you can create a new SqlParameter.
If you're using .NET 4, just create an SqlParameter array and add your created parameters to it in your loop. After your loop ends, you can use the "AddRange' method of your command's parameter collection to simply add the parameter array to your command.
This way, you're able to build a fully dynamic query that sanitizes inputs.
Related
I want to define a complete SQL statement condition after where through the linking implementation of string, because I am not sure how many conditions after where there are.
for (int i = 0; i < listView2.Items.Count; i++)
{
if (!is_first)
{
para += "maccount" +" "+ "=" + listView2.Items[i].Text;
is_first = true;
}
else
{
para += " or " + "maccount"+"="+ listView2.Items[i].Text;
}
}
MessageBox.Show(para);
string sql3 = "select maccount,msum from pharmaceutical_stocks where #para";
SqlParameter[] parameters3 = new SqlParameter[]
{
new SqlParameter("#para",para)
};
DataTable dt = sqlcontent.dt(sql3, parameters3);
I want to find data in the database by the information saved in each item in listview2。
But I get this exception:
System.Data.SqlClient.SqlException: An expression of non-Boolean type is specified in the context in which the condition should be used (near '#para').
The code above cannot work!.
Parameters cannot be used to replace blocks of text within the query, whether the text is a column name, a table name, operator or some combination of the previous elements.
They can be used only to transfer values to the database engine where they are properly used with the query text and the placeholders inside that query without a text-replace operation. So instead of trying to build a series of OR statements around the maccount field, you should use the IN clause and build an array of parameters. The single parameters placeholders can be constructed by code internally concatenating a string without worrying about Sql Injection.
At the end you insert the parameters placeholders (not the values) in the query text and pass the list with all defined parameters to your method
StringBuilder inText = new StringBuilder();
List<SqlParameter> prms = new List<SqlParameter>();
for(int i = 0; i < listView2.Items.Count; i++)
{
SqlParameter p = new SqlParameter("#p" + i, SqlDbType.NVarChar);
p.Value = listView2.Items[i].Text;
prms.Add(p);
inText.Append($"#p{i},");
}
if(inText.Length > 0) inText.Length--;
string sql3 = $#"select maccount,msum
from pharmaceutical_stocks
where maccount in({inText.ToString()})";
DataTable dt = sqlcontent.dt(sql3, prms.ToArray());
The idea behind my code is same as Steve's answer but my suggestion to use string.Join for building params string. Assuming parameters is SqlParameter[]
const string sql = "select maccount, msum from pharmaceutical_stocks where maccount in ({0})";
string sqlCommand = string.Format(sql, string.Join(",", parameters.Select(p => p.ParameterName)));
This way you don't worry about comma in the end. The only thing you still want to check if there are indeed some items in listView2
It's possible to perform this operation without "foreach" ???
I would understand if there are library functions to associate a parameter a list of data and perform N insert all together
String SQL_DETAIL = "INSERT INTO [ImportDetail] " +
" ([idMaster] " +
" ,[operation] " +
" ,[data]) " +
" VALUES " +
" (#a,#b,#c) ";
foreach (ImportDetail imp in this.lstImportDetail )
{
SqlCommand dettCommand = new SqlCommand(SQL_DETAIL, myTrans.Connection);
dettCommand.Transaction = myTrans;
dettCommand.Parameters.Add("a", SqlDbType.Int).Value = imp.IdMaster;
dettCommand.Parameters.Add("b", SqlDbType.NVarChar).Value = imp.Operation;
dettCommand.Parameters.Add("c", SqlDbType.NVarChar).Value = imp.Data;
i =i+ (int)dettCommand.ExecuteNonQuery();
}
Thank for help
If you were feeling frisky, you could create a user-defined table type and put the data from all your ImportDetail objects into a DataTable. This wouldn't totally alleviate the need for a loop, though -- it just makes it more efficient since you're only transforming data in memory instead of passing it to a remote database. Once you have your DataTable populated to match the schema of your table type, you can pass it as a parameter to a stored procedure to handle your INSERTs. The procedure can be made transactional if you'd like, and since your input data will already be in a table (of sorts), the SQL for the stored procedure should be pretty simple, as well.
For more info on user-defined table types and how they're used in .NET, check out this link: http://msdn.microsoft.com/en-us/library/bb675163.aspx
Going a little further, if this is something you have to do quite often, it's not out of the realm of possibility to turn this into some sort of extension method on IEnumerable to keep things DRY.
Is this the sort of solution you were looking for? It's a little hard for me to tell given the wording of the question.
You can define two array list:
the first will have the name of the parameter and the second its value:
ArrayList arrayName = new ArrayList { };
ArrayList arrayValue = new ArrayList { };
arrayName.Add("#Parameter1");
arrayName.Add("#Parameter2");
arrayValue.Add(value1);
arrayValue.Add(value2);
for (int i = 0; i < arrayName.Count; i++)
{
cmd.Parameters.AddWithValue(arrayName[i].ToString(), arrayValue[i]);
}
Then execute command
i have a list that i am pulling things out of to insert into a database. This is not going to be a web app so i have just been doing as follows:
string sqlStorage = (null,"asd"),
for (int i = 1; i < listsize; )
{
sqlStorage = sqlStorage + "(null,someVariableFromLoop)";
i++
}
string connString = "Server=localhost;...........";
MySqlConnection conn = new MySqlConnection(connString);
MySqlCommand command = conn.CreateCommand();
command.CommandText = #"INSERT INTO table1 VALUES " + tempSQLStorage;
etcetc...
However
"someVariableFromLoop"
is a large amount of text which includes all kinds of horrible code breaking characters. quotation marks etc etc.
So i looked into parameters (the way i should be doing SQL i know, i know), however i was unable to find a way to store these parameters inside the loop. i dont want to hit the DB every single iteration. I had a go at something along the lines of
"#variable"+i.toString();
but could not get it to work at all.
So does anyone have any idea how i would go about storing the parameters and the execute the query? Thanks in advance!
So i looked into parameters (the way i should be doing SQL i know, i know), however i was unable to find a way to store these parameters inside the loop. i dont want to hit the DB every single iteration. I had a go at something along the lines of
"#variable"+i.toString();
but could not get it to work at all.
Well, what was the error you received? Because that's the way you do it. Here's an example for MSSQL and I know the technique works, because I've done similar before:
int i = 0;
List<string> clauses = new List<string>() {"(#key0, #value0)"};
List<SqlParameter> paramList = new List<SqlParameter> {
new SqlParameter("#key0", DBNull.Value),
new SqlParameter("#value0", "asd")
};
for (i = 1; i < listSize; i++) {
clauses.Add("(#key" + i + ", #value" + i + ")");
paramList.Add(new SqlParameter("#key" + i, someKey));
paramList.Add(new SqlParameter("#value" + i, someValue);
}
SqlConnection conn = new SqlConnection(connString);
SqlCommand command = new SqlCommand(conn, #"INSERT INTO table1 VALUES " + String.Join(", ", clauses);
foreach(SqlParameter param in paramList) command.Parameters.Add(param);
command.ExecuteNonQuery();
Note, above code is quick and dirty. Obviously using statements and various other best practices should be incorporated as well for production code.
Also look at this: How do you use the MySql IN clause. It has an example of dynamically creating and passing parameters to the query, but for an SELECT...IN clause vs. INSERT...VALUES.
To ensure secure code (and avoid malformed queries), use SQL Command objects with Parameters. There is nothing horribly wrong with executing the command once for every record - a little extra overhead for round-trips over the network, but if the text is long you might have to do this anyway since queries do have a character limit.
Currently, I am creating an SQL Query by doing something like
string SQLQuery = "SELECT * FROM table WHERE ";
foreach(word in allTheseWords)
{
SQLQuery = SQLQuery + " column1 = '" + word + "' AND";
}
I understand that this can lead to an SQL Injection attack. I don't know how to pass an array as a parameter
where report in #allTheseWords
===========
I am using SQL Server 2012
Unfortunately, you cannot pass an array as a parameter without adding a user-defined type for table-valued parameters. The simplest way around this restriction is to create individually named parameters for each element of the array in a loop, and then bind the values to each of these elements:
string SQLQuery = "SELECT * FROM table WHERE column1 in (";
for(int i = 0 ; i != words.Count ; i++) {
if (i != 0) SQLQuery += ",";
SQLQuery += "#word"+i;
}
...
for(int i = 0 ; i != words.Count ; i++) {
command.Parameters.Add("#word"+i, DbType.String).Value = words[i];
}
You can also create a temporary table, insert individual words in it, and then do a query that inner-joins with the temp table of words.
Here is the recommendation from Microsoft:
Use Code Analysis to detect areas in your Visual Studio projects that are prone to sql injection;
Refer to the article on how to reduce risk of attack:
On short they talk about:
using a stored procedure.
using a parameterized command string.
validating the user input for both type and content before you build the command string.
Btw, you can enable static analysis as part of your build process and configure it so that when a security rule is broken, the build also breaks. Great way to make sure your team writes secure code!
Using ADO you can do it with the help of params
SqlConnection Con = new SqlConnection(conString);
SqlCommand Com = new SqlCommand();
string SQLQuery = "SELECT * FROM table WHERE ";
int i=1;
foreach(word in words)
{
Com.Parameters.Add("#word"+i.ToString(),SqlDbType.Text).Value = word;
SQLQuery = SQLQuery + " column1 = '#word"+i.ToString()+"' AND ";
i++;
}
Com.CommandText =SQLQuery;
For SQL Server, you'd use a Table-Valued Parameter. SQL has one structure that represents a collection of multiple items of the same type. It's called a table. It doesn't have arrays.
Of course, your supposed updated query:
where report in #allTheseWords
Isn't equivalent to your original query, but may be closer to the intent. In the query constructed using AND, you're saying that the same column, in the same row has to be equal to multiple different words. Unless all of the words are equal, this will never return any rows. The updated query answers whether any of the words match, rather than all.
You need to use prepared statements. The way those are handled is that you write your query and put placeholders for the values you want to use. Here's an example:
SELECT * FROM table WHERE column1 = #word
You then have to go through a prepare phase where the SQL engine knows it will need to bind parameters to the query. You can then execute the query. The SQL engine should know when and how to interpret the parameters you bind to your query.
Here's some code to do that:
SqlCommand command = new SqlCommand(null, rConn);
// Create and prepare an SQL statement.
command.CommandText = "SELECT * FROM table WHERE column1 = #word";
command.Parameters.Add ("#word", word);
command.Prepare();
command.ExecuteNonQuery();
I combine the use of params with HtmlEncoding(to get rid of special characters where not needed). Give that a shot.
using (SqlConnection conn = new SqlConnection(conString))
{
string sql = "SELECT * FROM table WHERE id = #id";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.paramaters.AddWithValue("#id", System.Net.WebUtility.HtmlEncode(id));
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
}
}
}
I want to update multiple rows like below
update mytable set s_id = {0} where id = {1}
(Here s_id is evaluated based on some complex logic).
For performance reason, updates should happen in batches. Is there any way to batch the update statements and execute the batch through single execute statements? I know in JAVA we can do this through JDBC. Is there similar way in C#?
Thanks in advance
Yes, you can use an SqlDataAdapter.
The SqlDataAdapter has InsertCommand and UpdateCommand properties which allow you to specify an SQLCommand to use to insert new rows into the database and an SqlCommand to update rows in the database respectively.
You can then pass a DataTable to the Update method of the dataadapter, and it will batch up the statements to the server - for rows in the DataTable that are new rows, it executes the INSERT command, for modified rows it executes the UPDATE command.
You can define the batch size using the UpdateBatchSize property.
This approach allows you to deal with large volumes of data, and allows you to nicely handle errors in different ways, i.e. if an error is encountered with a particular update, you can tell it to NOT throw an exception but to carry on with the remaining updates by setting the ContinueUpdateOnError property.
Yes, you can build a plain-text SQL command (parameterized for security), like this:
SqlCommand command = new SqlCommand();
// Set connection, etc.
for(int i=0; i< items.length; i++) {
command.CommandText += string.Format("update mytable set s_id=#s_id{0} where id = #id{0};", i);
command.Parameters.Add("#s_id" + i, items[i].SId);
command.Parameters.Add("#id" + i, items[i].Id);
}
command.ExecuteNonQuery();
Use a StringBuilder (System.Text.StringBuilder) to build your Sql, such as:
StringBuilder sql = new StringBuilder();
int batchSize = 10;
int currentBatchCount = 0;
SqlCommand cmd = null; // The SqlCommand object to use for executing the sql.
for(int i = 0; i < numberOfUpdatesToMake; i++)
{
int sid = 0; // Set the s_id here
int id = 0; // Set id here
sql.AppendFormat("update mytable set s_id = {0} where id = {1}; ", sid, id);
currentBatchCount++;
if (currentBatchCount >= batchSize)
{
cmd.CommandText = sql.ToString();
cmd.ExecuteNonQuery();
sql = new StringBuilder();
currentBatchCount = 0;
}
}
Create a set of those updates (with the id's filled in), separate them by semicolon in one string, set the resulting string to a SqlCommand's CommandText property, then call ExecuteNonQuery().