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.
Related
I am trying to modify a table data using a SQL statement
foreach (Words words in Words_DB.Records)
{
string _IPAUS = words.IPAUS;
string _IPAUK = words.IPAUK;
query = "UPDATE Words SET IPAUK='" + _IPAUK + "',IPAUS='" + _IPAUS + "' WHERE WORD='" + words.Word + "'";
command.Parameters.Clear();
command.CommandText = query;
//command.Parameters.AddWithValue("#IPAUK", _IPAUK);
//command.Parameters.AddWithValue("#IPAUS", _IPAUS);
//command.Parameters.AddWithValue("#WORD", words.Word);
int a = command.ExecuteNonQuery();
}
A example of query is UPDATE Words SET IPAUK='ɑːd.vɑːk',IPAUS='ɑːrd.vɑːrk' WHERE WORD='aardvark'
The problem is when a read the database data I receive :
But, when I use the MySql Tools to execute the Query the result is right.
What I am doing wrong?
Regards
The question concatenates raw input to generate a SQL query which exposes to SQL injection and bugs like this one. If _IPAUK contained '; -- all the data in that column would be lost.
In this case it seems the code is trying to pass Unicode data using ASCII syntax, resulting in mangled data.
The solution to both SQL injection and conversion issues is to use parameterized queries. In a parameterized query, the actual parameter values never become part of the query itself. The server compiles the SQL query into an execution plan and executes that using the parameter values.
await using var connection = new MySqlConnection(connString);
await connection.OpenAsync();
// Insert some data
using (var cmd = new MySqlCommand())
{
cmd.Connection = connection;
cmd.CommandText = "UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#Word";
cmd.Parameters.AddWithValue("IPAUK", words.IPAUK);
cmd.Parameters.AddWithValue("IPAUS", words.IPAUS);
cmd.Parameters.AddWithValue("Word", words.Word);
await cmd.ExecuteNonQueryAsync();
}
The example uses the open source MySQLConnector ADO.NET Driver instead of Oracle's somewhat ... buggy driver.
The code can be simplified even more by using Dapper to construct the command, parameters and handle the connection automagically. Assuming words only has the IPAUK, IPAUS and Word properties, the code can be reduced to three lines :
var sql="UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#Word";
await using var connection = new MySqlConnection(connString);
await connection.ExecuteAsync(sql,words);
Dapper will construct a MySqlCommand, add parameters based on the properties of the parameter object (words), open the connection, execute the command and then close the connection
Thanks a lot for your helps.
This is my final code working properly.
string query = "UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#WORD";
var command = DatabaseConnection.MySql_Connection.CreateCommand();
try
{
foreach (Words words in Words_DB.Records)
{
MySqlParameter IPAUSp = new MySqlParameter("#IPAUS", MySqlDbType.VarChar, 60);
MySqlParameter IPAUKp = new MySqlParameter("#IPAUK", MySqlDbType.VarChar, 60);
MySqlParameter WORD = new MySqlParameter("#WORD", MySqlDbType.VarChar, 50);
command.Parameters.Clear();
command.CommandText = query;
command.Parameters.AddWithValue(IPAUKp.ToString(), words.IPAUK);
command.Parameters.AddWithValue(IPAUSp.ToString(), words.IPAUS);
command.Parameters.AddWithValue(WORD.ToString(), words.Word);
int a = command.ExecuteNonQuery();
}
}
Try it like this:
command.CommandText = "UPDATE Words SET IPAUK= #IPAUK, IPAUS= #IPAUS WHERE WORD= #Word;";
// Match these to the column type and length in the DB
command.Parameters.Add("#IPAUK", MySQlDbType.VarChar, 30);
command.Parameters.Add("#IPAUS", MySQlDbType.VarChar, 30);
command.Parameters.Add("#Word", MySQlDbType.VarChar, 30);
foreach (Words words in Words_DB.Records)
{
command.Parameters["#IPAUK"].Value = words.IPAUK;
command.Parameters["#IPAUS"].Value = words.IPAUS;
command.Parameters["#Word"].Value = words.Word;
command.ExecuteNonQuery();
}
Notice how the above minimizes the work done in the loop, which should improve performance, while also fixing the HUGE GAPING SECURITY ISSUE in the question from using string concatenation to build the query.
Separately, I have the impression Words_DB.Records is the result of a prior query. It's highly likely you could eliminate this entire section completely by updating the prior query to also do the update in one operation on the server. Not only would that greatly reduce your code, it will likely improve performance here by multiple orders of magnitude.
I am writing a small program using an SQL database. The table name is StudentInfo.
I need to know the SQL code for the following
for (n=0; n<nRows; n++) {
string sql1="update StudentInfo set Position=" + n + " where <this has to be the row number>";
}
nRows is number of rows.
How can I get the row number for the above code?
best way to do this is to create a stored procedure in the database and use your code to pass the relevent information to the server
In order to accomplish this task you'll want to create a Stored Procedure or build a Query that actually accepts parameters. This will help you pass variables between, your method of concatenation will actually cause an error or become susceptible to SQL Injection attacks.
Non Parameter SQL Command:
using(SqlConnection sqlConnection = new SqlConnection("Database Connection String Here"))
{
string command =
"UPDATE Production.Product " +
"SET ListPrice = ListPrice * 2 " +
"WHERE ProductID IN " +
"(SELECT ProductID " +
"FROM Purchasing.ProductVendor" +
"WHERE BusinessEntityID = 1540);" +
using (SqlCommand sqlCommand = new SqlCommand(command, sqlConnection))
{
int execute = command.ExecuteNonQuery();
if( execute <= 0)
{
return false;
}
else
{
return true;
}
}
}
That method is essentially creation a connection, running our SQL Command, then we are using an integer to verify that it did indeed run our command successful. As you can see we simply using SQL to run our command.
The other important thing to note, you can't create a sub-query with an update; you have to create an update then run a select as the sub-query to hone in more specific data across so you can span across tables and so on.
The other alternative would be to use a parameter based query, where your passing variables between SQL and your Application.
I won't post code to that, because I believe you wrote the C# loop to demonstrate what you would like SQL to do for you. Which is only update particular rows; based on a specific criteria.
If you could post additional information I'd be more then happy to help you. But I'm just going to post what I believe you are trying to accomplish. Correct me if I'm wrong.
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 have the following code in asp.net:
using (OleDbCommand command = dbConnW.CreateCommand())
{
string CreateTableK = null;
CreateTableK += "Create Table DSKKAR00 (DSK_ID c(10),DSK_KIND N(1),MON_PYM C(3))";
OleDbCommand cmdCreateTable = new OleDbCommand(CreateTableK, dbConnW);
cmdCreateTable.ExecuteNonQuery();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(WorkRoomNo + ",");
sb.Append("1,");
sb.Append(",");
OleDbCommand cmd3 = new OleDbCommand("Insert into DSKKAR00 (DSK_ID,DSK_KIND,MON_PYM) Values (" + sb.ToString() + ")", dbConnW);
cmd3.ExecuteNonQuery();
But I have the following error:
Syntax error
In addition to what Chris has offered, you are starting your CREATE TABLE with a NULL string variable, then doing a += to it. From what I remember, a NULL += "anystring" will remain a null value... You might be crashing right there too.
Although VFP is not really suceptible to SQL Injection like other SQL engines, its good habit to do parameterizing. When you do, use "?" as a place-holder for the value you want to insert, and add parameters in the same order sequence as the "?" represent.
string CreateTableK =
"Create Table DSKKAR00 (DSK_ID c(10),DSK_KIND N(1),MON_PYM C(3))";
OleDbCommand cmdCreateTable = new OleDbCommand(CreateTableK, dbConnW);
cmdCreateTable.ExecuteNonQuery();
string MyInsert =
"insert into DSKKAR00 ( dsk_id, dsk_kind, mon_pym ) values ( ?, ?, ? )";
OleDbCommand cmd3 = new OleDbCommand( MyInsert, dbConnW);
cmd3.Parameters.AddWithValue( "parmSlot1", WorkRoomNo );
cmd3.Parameters.AddWithValue( "parmSlot2", 1);
cmd3.Parameters.AddWithValue( "parmSlot3", 'tst' ); // or whatever variable to put
cmd3.ExecuteNonQuery();
First off, any time you have an error it's usually best to post the entire error message you get.
Also, when trying to debug a query problem, you should emit the actual query being sent to your server/database and inspect it. This way you can find various problems like too many commas.
Speaking of which, looking at your code, you are concatenating a String and it really looks like you have way too many commas.
The emitted query looks like it will be:
insert into DSKKAR00(DSK_ID, DSK_KIND, MON_PYM) VALUES( X,1, ,)
where X is the value of your WorkRoomNo variable.
Obviously, that isn't valid syntax and would result in the error you've seen. The commas indicate there are 4 values being passed, but the insert query only identifies 3 columns.
The next issue has to do with the column definitions themselves. The first column of that table is a c(10); the third is a c(3). I'm a little rusty, but aren't those character fields?
If so then you need to adjust your string builder to add the appropriate quotes around the values...
Which leads us to the final problem: Don't use String concatentation to build queries. Use Parameterized queries
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.