简体   繁体   中英

Update multiple rows into SQL table

Suppose there is a fully populated array of data String[n][3] myData.

I want to do this:

for (String[] row : myData)
{
   SQL = "update mytable set col3 = row[2]
   where col1 = row[0] and col2=row[1];" 
}

Obviously I've left a lot out, but I want to express the idea as succinctly as possible.

Is there a simple way of doing this in a single DB command? How about a not so simple way?

EDITS: Data is not coming from another table (it's a web form submission - Multiple Choice exam)
Seeing as the app is web facing, It's got to be injection proof. Parameterized Queries are my preferred way of going.
I'm using MS-SQL Server 2005

EDIT:Closing, and re-asking as Multiple DB Updates:

EDIT: Re-opened, as this appears to be a popular question

It depends on what database you are using. If you're using SQL Server 2008, you can use stored procedure TABLE parameters . This allows you to pass all your values into the stored procedure in a single table, then you can do:

update mytable set mytable.col1 = @tbl.col1
  from mytable 
  inner join @tbl on mytable.col2 = @tbl.col2

If you're using SQL Server 2005, you can use XML. Format your values as XML, then use XQuery statements (ie, 'nodes' and 'value') to parse out the XML. This can also be done in a single SQL statement, and it doesn't require a stored procedure.

If you are using Sql Server you can use SqlBulkCopy. You would first have to put your data in a DataTable, which would be pretty easy since you already have it in a string array.

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx

You can make a big string like:

for (String[] row : myData)
{
   SQL += "update mytable set col3 = row[2]
   where col1 = row[0] and col2=row[1];" 
}

sqlDriver.doInsertQuery(SQL); // change this to your way of inserting into the db

And just commit it all at once. I'm not very good with SQL so that's how i would do it.

The sql engine will just split it by the ';' and do separate inserts on its own. It's ok to add it all in a string though. It's kind the same as if u copy a big string with multiple updates/inserts into the sql prompt

This may not be the answer you want, but opening a transaction, executing your statements and then committing the transaction would, from a database point of view, do what you describe.

The state of the database does not change for other users of the database until the transaction has been completed, and that probably is the preferred effect.

That looks like you want to make an update A, over rows that has coditions B and C. (A, B, C) are stored as tuples (rows) in myData. Isn't it?

Maybe (if you're using Microsoft SQL Server... I don't know if it exists in Oracle, could be) you can use a JOIN with an UPDATE. You can declare an update over a table joined with another one. If myData comes from another table then you could do (it's not the correct syntax) :

UPDATE whatchanges wc INNER JOIN changes c ON <yourcondition>
SET wc.col1 = c.newvalue
WHERE ....

(if you want to apply all changes in "changes" table you don't have to use WHERE of course, the INNER JOIN already has selected the correct rows).

Of course there are limitations to this kind of update. And it's MS SQL proprietary. So if it's your case I'd suggest to look for it on MS web (keywords: UPDATE and JOIN)

If you are using Enterprise Library in your data access layer, you can create the transaction in .Net, iterate through your procedure calls, then commit/rollback all from .Net.

DbTransaction transaction = connection.BeginTransaction();
try
{
    for (String[] row : myData)
    {
        ListDictionary params = new Specialized.ListDictionary();
        params.add("@col3", row[2]);
        params.add("@col1", row[0]);
        params.add("@col2", row[1]);
        executeNonQuery("myUpdateProcedure", params);
    }

    transaction.commit();

}
catch(Exception ex)
{
    transaction.rollback();
    throw ex;
}
finally
{

    connection.close();
}

If for whatever reason you can't perform the update using one of the methods suggested above, the highly inefficient approach below would probably work for you.

SQL = "Update myTable Set Col3 = Case " 
for (String[] row : myData)
{
   SQL += "When Col1 = " + Row[0] + " and Col2 = " + Row[1] + " then " + row[2] + " "   
}
SQL + = "Else Col3 end"  

Not really. You could create the string with the same loop, then pass your values as parameters, but that will still be multiple database commands.

for each whatever
    sql += "UPDATE ... ;"
end for
execute (sql)

I suspect you will need to use multiple SQL statements. You may find a wrapper to handle the minutiae but underneath I'd imagine it'd iteratively run a SQL statement for each UPDATE.

emit an update that goes against a values table:

UPDATE myTable SET col3=c FROM myTable JOIN (
  SELECT 1 as a, 2 as b, 'value1' as c UNION ALL
  SELECT 3 as a, 4 as b, 'value2' as c -- etc...
) x ON myTable.col1=x.a AND myTable.col2=x.b

so you just put this together like this:

// make one of these for each row
String.Format("SELECT {0} as a, {1} as b, '{2}' as c", 
  row[0], row[1], row[2].Replace("'","''")) 

// put it together
string expr = "UPDATE myTable SET col3=c FROM myTable JOIN (" +
  String.Join(stringformatarray, " UNION ALL ") +
  ") x ON myTable.col1=x.a AND myTable.col2=x.b"

or you can use StringBuilder to put this together.

and then, of course, you execute this one string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM