i have a question regarding an efficient way to update multiple rows via SQL.
Basiclly i have a query i need to run on different RowIDs:
UPDATE TableName SET Column = (some number) WHERE RowID = (some number)
if to be more specific this is a better example:
UPDATE TableName SET Column = 5 WHERE RowID = 1000
UPDATE TableName SET Column = 10 WHERE RowID = 1001
UPDATE TableName SET Column = 30 WHERE RowID = 1002
..
I'd like to know how should i build the update query command on C# (or just give me an example of the resulted query i should get to) so once i use ExecuteQuery it will run all of these commands at one piece and not by executing each command.
edited:
I have another problem, can you also explain what about dynamic situation in which not necessarly the row i want to update exist already, in that case i need to insert instead of update. to explain better, back to my example lets say i want to do
UPDATE TableName SET Column = 5 WHERE RowID = 1000
INSERT INTO TableName [RowID, Column] VALUES (1001, 20)
UPDATE TableName SET Column = 30 WHERE RowID = 1002
..
The meaning of this is that i need to check if the row exist, if so i'd use update otherwise i'll have to insert it.
Thank you!
You could use a DataTable to store your records, insert, delete or change rows and update all changes in one batch by using SqlDataAdapter's UpdateBatchSize(0 means no limit):
public static void BatchUpdate(DataTable dataTable,Int32 batchSize)
{
// Assumes GetConnectionString() returns a valid connection string.
string connectionString = GetConnectionString();
// Connect to the AdventureWorks database.
using (SqlConnection connection = new
SqlConnection(connectionString))
{
// Create a SqlDataAdapter.
SqlDataAdapter adapter = new SqlDataAdapter();
// Set the UPDATE command and parameters.
adapter.UpdateCommand = new SqlCommand(
"UPDATE Production.ProductCategory SET "
+ "Name=#Name WHERE ProductCategoryID=#ProdCatID;",
connection);
adapter.UpdateCommand.Parameters.Add("#Name",
SqlDbType.NVarChar, 50, "Name");
adapter.UpdateCommand.Parameters.Add("#ProdCatID",
SqlDbType.Int, 4, "ProductCategoryID");
adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
// Set the INSERT command and parameter.
adapter.InsertCommand = new SqlCommand(
"INSERT INTO Production.ProductCategory (Name) VALUES (#Name);",
connection);
adapter.InsertCommand.Parameters.Add("#Name",
SqlDbType.NVarChar, 50, "Name");
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
// Set the DELETE command and parameter.
adapter.DeleteCommand = new SqlCommand(
"DELETE FROM Production.ProductCategory "
+ "WHERE ProductCategoryID=#ProdCatID;", connection);
adapter.DeleteCommand.Parameters.Add("#ProdCatID",
SqlDbType.Int, 4, "ProductCategoryID");
adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;
// Set the batch size.
adapter.UpdateBatchSize = batchSize;
// Execute the update.
adapter.Update(dataTable);
}
}
http://msdn.microsoft.com/en-us/library/aadf8fk2.aspx
I assume you're misunderstanding how the dbms works internally. This
UPDATE TableName SET Column = 5 WHERE RowID = 1000;
UPDATE TableName SET Column = 5 WHERE RowID = 1002;
is the same as
UPDATE TableName SET Column = 5 WHERE RowID IN(1000,2002);
The dbms will update all affected records one by one anyway even if you would write a statement like UPDATE table SET value=1 which would affect every record in the table. By updating in one batch you ensure that all updates(deletes,inserts)are submitted to the database instead of one roundtrip for every statement.
Use MERGE:
MERGE INTO TableName
USING (
VALUES (1000, 5),
(1001, 10),
(1002, 30)
) AS source (RowID, Column_name)
ON TableName.RowID = source.RowID
WHEN MATCHED THEN
UPDATE
SET Column_name = source.Column_name
WHEN NOT MATCHED THEN
INSERT (RowID, Column_name)
VALUES (RowID, Column_name);
Rather than hard-coding/dynamic SQL, the MERGE statement could be encapsulated into a stored proc that takes a table-valued parameter.
Related
I am new to DataAdapter. So please bear with me.
Why is the Update SqlCommand and UPDATE SQL statement required and Value given to the Update command (using parameters) when we mandatorily have to change the value in the datatable by actually assigning it. Which means only providing the Update SQL does not work?
We have to do this and only then the database gets updated when we do DataAdapter.Update()?
DataRow categoryRow = categoryTable.Rows[0];
categoryRow["CategoryName"] = "New Beverages";
Because like here the column value of Rows[0] is changed manually. But, if there were 10 rows in the DataTable and we want to change values in more than 1 row based on the UPDATE SQL statement condition.
This is the full code which works, but i dont understand the use of having a UPDATE Sqlcommand when the actual work is done by manually changing the value in the column of a specific row.
private static void AdapterUpdate(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlDataAdapter dataAdpater = new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories",
connection);
dataAdpater.UpdateCommand = new SqlCommand(
"UPDATE Categories SET CategoryName = #CategoryName " +
"WHERE CategoryID = #CategoryID", connection);
dataAdpater.UpdateCommand.Parameters.Add(
"#CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(
"#CategoryID", SqlDbType.Int);
parameter.SourceColumn = "CategoryID";
parameter.SourceVersion = DataRowVersion.Original;
DataTable categoryTable = new DataTable();
dataAdpater.Fill(categoryTable);
DataRow categoryRow = categoryTable.Rows[0];
categoryRow["CategoryName"] = "New Beverages";
dataAdpater.Update(categoryTable);
Console.WriteLine("Rows after update.");
foreach (DataRow row in categoryTable.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
This is for other people who face the same doubt.
Just figured out...
The Update SQL is for the update in Database and it will update the values given/updated in the DataTable column. Also only those columns will be updated in the Database which are mentioned in the Update SQL statement SET Columns. So if you have updated a value in a DataTable column, but it is not one of the SET columns of the UPDATE SQL, then that value is not updated to the Database.
So the SQL is also required along with the updated Datatable column values.
I hope i am clear :)
How to write mysql update query for updating multiple rows of datagridview in single statement by passing parameters and then executing all together in c#.net windows application
for example create update query like is given below
foreach(DatagridViewRow dr in datagridview)
{
sql = "update table1 set name=#name where id=#id; update table1 set name=#name where id=#id; update table1 set name=#name where id=#id;"
}
How to create a sql query with parameters? So that after creation of query; we can execute it together to make changes in multiple rows.
You don't need to put multiple Update queries together.
Here take a look:
string sql = "update table1 set name=#name where id=#id";
MySqlParameter pId = new MySqlParameter("#id", SqlDbType.BigInt);
MySqlParameter pName = new MySqlParameter("#name", SqlDbType.NVarchar);
cmd.Parameters.Clear();
cmd.CommandText = query;
cmd.Parameters.Add(pName);
cmd.Parameters.Add(pId);
foreach(DatagridViewRow dr in datagridview)
{
pId.Value = dr.Rows["iDColumn"].ToString();
pName.Value = dr.Rows["nameColumn"].ToString();
cmd.ExecuteNonQuery();
}
Hope this helps.
I built a Database (Microsoft SqlServerCe.4.0) using Visual Studio and one table containing two fields:
id: int, primary key, not null, unique, no default value, identity
nom, we don't really care about this one
Then I built a DataSet containing this table as a DataTable and I have a DataAdapter like this :
marque_adapter = factory.CreateDataAdapter();
command = connection.CreateCommand();
command.CommandText = "SELECT * FROM " + DB_TABLE_MARQUE + ";";
marque_adapter.SelectCommand = command;
command = connection.CreateCommand();
command.CommandText = "UPDATE " + DB_TABLE_MARQUE + " SET nom = #nom WHERE id = #id;";
CreateAndAddParameterFromSource(command, "id", "id");
CreateAndAddParameterFromSource(command, "nom", "nom");
marque_adapter.UpdateCommand = command;
command = connection.CreateCommand();
command.CommandText = "DELETE " + DB_TABLE_MARQUE + " WHERE id = #id;";
CreateAndAddParameterFromSource(command, "id", "id");
marque_adapter.DeleteCommand = command;
command = connection.CreateCommand();
command.CommandText = "INSERT INTO " + DB_TABLE_MARQUE + " (nom) VALUES (#nom);";
CreateAndAddParameterFromSource(command, "nom", "nom");
marque_adapter.InsertCommand = command;
//...
data = new DataSet();
marque_adapter.Fill(data, DB_TABLE_MARQUE);
The problem arises when I try to insert a new row.
I do :
table.NewRow()
set the "nom" field
table.Rows.Add(newRow)
adapter.Update(dataSet, tableName)
If I don't do anything else, I have issues later when I try to get the ID of this row (I guess it will set it somewhere between the four instructions above).
I was expecting the DataTable to take care of generating one, but ...
So I tried remindind the DataTable to take care of the auto incrementing :
idColumn.Unique = true;
idColumn.AutoIncrement = true;
Now it works the first time, but when I run the program a second time, it starts counting from one again and I'm told that the ID should be unique. If I delete the database (the copy of the sdf file made by Visual), or if I delete the rows manually using Visual, it runs well the first time, and I get the same error after.
The problem really is when I try to save my DataSet, particularly when adding new rows (selecting, updating, deleting is fine).
Obviously I didn't get how to manage primary keys when the DataTable and the database are involved (the datatable alone is ok).
Particularly to sync the two ...
What did I miss ?
I am quite sure I have misunderstood something.
According to MSDN,
Bydefault, AcceptChanges is called implicitly after an update, and the original values in the row, which may have been AutoIncrement values assigned by ADO.NET, are lost.
So you need to create a strategy to merge the AutoIncremented value Either via ADO or getting back the incremented Id from Sql as output parameter and then merge the Identity column value as indicated in this MSDN Article.
I have the following two MySQL tables:
questions:
question_id (PK, AI), module_id (FK), author_id (FK), approved, question, correct_answer_id (FK)
answers:
answer_id (PK, AI), question_id (FK), answer
I want to be able to insert a new row in the 'questions' table and multiple rows in the 'answers' tables.
The new rows in the 'answers' table should have the same 'question_id' as the newly generated 'question_id' value in the 'questions' row. Also, the 'correct_answer_id' field in the 'questions' table should equal the 'answer_id' of the first row inserted in the 'answers' table.
Is there a more efficiently way to do this than the following steps?:
insert values (module_id, author_id, approved, question) in
'questions'
get last 'question_id' in 'questions'
insert values (question_id, answer) in 'answers'
update value (correct_answer_id) in 'questions'
code:
string connStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
MySqlConnection conn = new MySqlConnection(connStr);
string queryUpdateQuestions = "INSERT INTO questions (module_id, author_id, approved, question) VALUES (#module_id, #author_id, #approved, #question)";
MySqlCommand cmdUpdateQuestions = new MySqlCommand(queryUpdateQuestions, conn);
cmdUpdateQuestions.Parameters.Add("#module_id", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#module_id"].Value = ddlModules.SelectedValue.ToString();
cmdUpdateQuestions.Parameters.Add("#author_id", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#author_id"].Value = Session["UserID"].ToString();
cmdUpdateQuestions.Parameters.Add("#approved", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#approved"].Value = 'N';
cmdUpdateQuestions.Parameters.Add("#question", MySqlDbType.VarChar);
cmdUpdateQuestions.Parameters["#question"].Value = txtQuestion.Text;
try
{
conn.Open();
cmdUpdateQuestions.ExecuteNonQuery();
}
catch
{
lblError.Text="Unable to add question.";
}
finally
{
conn.Close();
}
//????? = get last question_id in 'questions'
int a = Convert.ToInt32(ddlNoOfAnswers.SelectedValue.ToString());
for (int b=1; b <= a; b++)
{
string queryUpdateAnswers = "INSERT INTO answers (question_id, answer) VALUES (#question_id, #answer)";
MySqlCommand cmdUpdateAnswers = new MySqlCommand(queryUpdateAnswers, conn);
cmdUpdateAnswers.Parameters.Add("#answer", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters["#answer"].Value = ((TextBox)this.FindControl("txtAnswer" + b)).Text;
cmdUpdateAnswers.Parameters.Add("#question_id", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters["#question_id"].Value = ?????;
try
{
conn.Open();
cmdUpdateAnswers.ExecuteNonQuery();
}
catch
{
lblError.Text="Unable to add answer.";
}
finally
{
conn.Close();
}
}
//update 'correct_answer_id' in 'questions'
Some simplification is possible. First of all you need to enclose all of your commands inside a transaction because this is the classical case where the records inserted are in strictly relationships and it doesn't make sense to have some partially completed set of records.
using(MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
using(MySqlTransaction tr = conn.BeginTransaction())
{
...
// MySqlCommand code goes here
...
tr.Commit();
}
}
Now, you could change your insert question sql to add a second statement that returns the last id inserted
string queryUpdateQuestions = #"INSERT INTO questions (.....);
SELECT LAST_INSERT_ID()";
using(MySqlCommand cmdUpdateQuestions = new MySqlCommand(queryUpdateQuestions, conn, tr))
{
// build the parameters for the question record
......
// Instead of ExecuteNonQuery, run ExecuteScalar to get back the result of the last SELECT
int lastQuestionID = Convert.ToInt32(cmdUpdateQuestions.ExecuteScalar());
..
}
Notice how, at the MySqlCommand constructor, is passed the reference to the current transaction. This is required to work with an connection that has a transaction opened.
Things are a bit more complex for the second part. The same trick to add a second sql statement could be applied also to the loop that insert the answers, but you need to loop backward if the first question is the correct one
string queryUpdateAnswers = #"INSERT INTO answers (question_id, answer)
VALUES (#question_id, #answer);
SELECT LAST_INSERT_ID()";
using(MySqlCommand cmdUpdateAnswers = new MySqlCommand(queryUpdateAnswers, conn, tr))
{
// next move the loop inside the using and prepare the parameter before looping to
// to avoid unnecessary rebuild of the parameters and the command
cmdUpdateAnswers.Parameters.Add("#answer", MySqlDbType.VarChar);
cmdUpdateAnswers.Parameters.Add("#question_id", MySqlDbType.Int32);
int lastAnswerID = 0;
// Loop backward so the last answer inserted is the 'correct' one and we could get its ID
for (int b=a; b >= 1; b--)
{
cmdUpdateAnswers.Parameters["#answer"].Value = ((TextBox)this.FindControl("txtAnswer" + b)).Text;
cmdUpdateAnswers.Parameters["#question_id"].Value = lastQuestionID;
lastAnswerID = Convert.ToInt32(cmdUpdateAnswers.ExecuteScalar());
}
....
}
Now you could run the last command that update the question with the lastAnswerID
(A last note, I suppose that the fields question_id and answer_id are of type numeric, not varchar, this requires that the parameters for these fields will be an Int32 not a varchar)
Yes, the approach you outline is the most efficient. You will need to retrieve the value assigned to the AUTO_INCREMENT column of each row INSERTED. But be careful how you retrieve that value.
insert a row into 'questions' table
retrieve last_insert_id value assigned to AUTO_INCREMENT column
insert row to 'answers' table, using retrieved value for 'question_id' column
retrieve last_insert_id value immediately following insert of "correct answer" row
update row in 'questions' to set 'correct_answer_id' column
MySQL provides the LAST_INSERT_ID() function. That's the mechanism that MySQL provides to retrieve the value assigned to an AUTO_INCREMENT column, following the successful execution of an INSERT statement. (With singleton inserts, it's very straightforward; it just has to be called immediately following the insert.)
Ref: http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id
A lot of client libraries provide a builtin function to do this, so it's not necessary to prepare and execute a separate SELECT statement. (For example, with PHP, PDO provides lastInsertId, mysqli provides $insertid. It's likely that the C# connector for MySQL has a similar function.)
I have been trying to use OleDbDataAdapter to update a DataTable but got confused about the commands.
Since I sometimes get info from diffrent tables I can't use a CommandBuilder.
So I have tried to create the commands on my on but found it hard with the parameters.
DataTable.GetChanges returns rows that needs to use an INSERT or an UPDATE command - I guess I can't distinct between them.
I need you to complete the following:
DataTable dt = new DataTable();
OleDbDataAdapter da = new OleDbDataAdapter();
// Here I create the SELECT command and pass the connection.
da.Fill(dt);
// Here I make changes (INSERT/UPDATE) to the DataTable (by a DataGridView).
da.UpdateCommand = new OleDbCommand("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES (#newVal1, #newVal2, ...) WHERE id=#id"); // How can I use the values of the current row (that the da is updating) as the parameters (#newVal1, #newVal2, id....)?
Thank you very much!
The data adapter can work in conjunction with the datatable. As such, I've actually wrapped mine together into a class and works quite well. Aside from the complexities of my stuff, here's a snippet that might help you along. When adding a parameter, you can identify the column source that the data is coming from FROM the DataTable. This way, when a record is internally identified as "Added" or "Updated" (or "Deleted"), when you build your SQL Insert/Update/Delete commands, it will pull the data from the columns from the respective rows.
For example. Say I have a DataTable, primary Key is "MyID" and has columns "ColX, ColY, ColZ". I create my DataAdapter and build out my select, update, delete commands something like... (? is a place-holder for the parameters)
DataAdapter myAdapter = new DataAdapter()
myAdapter.SelectCommand = new OleDbCommand();
myAdapter.InsertCommand = new OleDbCommand();
myAdapter.UpdateCommand = new OleDbCommand();
myAdapter.DeleteCommand = new OleDbCommand();
myAdapter.SelectCommand.CommandText = "select * from MyTable where MyID = ?";
myAdapter.InsertCommand.CommandText = "insert into MyTable ( ColX, ColY, ColZ ) values ( ?, ?, ? )";
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ?, ColZ = ? where MyID = ?";
myAdapter.DeleteCommand.CommandText = "delete from MyTable where MyID = ?";
Now, each has to have their respective "Parameters". The parameters have to be addded in the same sequence as their corresponding "?" place-holders.
// Although I'm putting in bogus values for preparing the parameters, its just for
// data type purposes. It does get changed through the data adapter when it applies the changes
OleDbParameter oParm = new OleDbParameter( "myID", -1 );
oParm.DbType = DbType.Int32;
oParm.SourceColumn = "myID"; // <- this is where it looks back to source table's column
oParm.ParameterName = "myID"; // just for consistency / readability reference
myAdapter.SelectCommand.Parameters.Add( oParm );
do similar for rest of parameters based on their types... char, int, double, whatever
Again, I have like a wrapper class that handles managment on a per-table basis... in brief
public myClassWrapper
{
protected DataTable myTable;
protected DataAdapter myAdapter;
... more ...
protected void SaveChanges()
{
}
}
Its more complex than just this, but during the "SaveChanges", The datatable and dataAdapter are in synch for their own purposes. Now, flushing the data. I check for the status of the table and then you can pass the entire table to the dataAdapter for update and it will cycle through all changed records and push respective changes. You'll have to trap for whatever possible data errors though.
myAdapter.Update( this.MyTable );
As it finds each "changed" record, it pulls the values from the Column Source as identified by the parameter that is found in the table being passed to the adapter for processing.
Hopefully this has given you a huge jump on what you are running into.
---- COMMENT PER FEEDBACK ----
I would put your update within a try/catch, and step into the program to see what the exception is. The message adn/or inner exception of the error might give more info. However, try to simplify your UPDATE to only include a FEW fields with the WHERE "Key" element.
Additionally, and I oopsed, missed this from first part answer. You might have to identify the datatable's "PrimaryKey" column. To do so, its a property of the DataTable that expects and array of columns that represent the primary key for the table. What I did was...
// set the primary key column of the table
DataColumn[] oCols = { myDataTbl.Columns["myID"] };
myDataTbl.PrimaryKey = oCols;
I would comment out your full update string and all its parameters for your UPDATE. Then, build it with just as simple as my sample of only setting 2-3 columns and the where clause
myAdapter.UpdateCommand.CommandText = "update MyTable set ColX = ?, ColY = ? where MyID=?";
Add Parameter object for "X"
Add Parameter object for "Y"
Add Parameter object for "MyID"
Pick fields like int or char so they have the least probability of problems for data type conversions, then, once that works, try adding all your "int" and "character" columns... then add any others. Also, which database are you going against. SOME databases don't use "?" as placeholder in the command but use "named" parameters, some using
"actualColumn = #namedCol"
or even
"actualColumn = :namedCol"
Hope this gets you over the hump...
You could use the String.Format Method to replace the #newVal1, #newVal2, ... in your code, like this da.UpdateCommand = new OleDbCommand(String.Format("UPDATE TABLE_NAME SET (COL1, COL2, ...) VALUES ({0}, {1}, ...) WHERE id=#id",OBJECT_ARRAY_CONTAINING_VALUES_FROM_THEDG));
[Eidt per comment]
To handle the row[0], row[1] you need a loop like:
for(i=0; i<rows.Count; i++)
{
da.UpdateCommand = new OleDbCommand(String.Format("UPDATE...",row[i]);
da.Update(dt);
}