tags:

views:

15850

answers:

10

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 http://stackoverflow.com/questions/184471/multiple-db-updates

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

A: 

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

fmsf
Now how do I parameterize it?
chris
A: 

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)
EndangeredMassa
Yeah, that's what I'm trying to avoid. I want a bulk update with parameterized queries in a single transaction.
chris
+1  A: 

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.

extraneon
A: 

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.

theraccoonbear
+1  A: 

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

Ted Elliott
+4  A: 

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.

Kevin Berridge
I do like the first option, I'll have to wait for an upgrade first.That's a pretty sweet feature.
chris
A: 

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 un update over a table joined with another one. If myData comes from another table then you coud do (it's not the correct syntax):

UPDATE whatchanges wc INNER JOIN changes c ON 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 updates. And it's MS SQL propietary. So if it's your case I'd suggest to look for it on MS web (keywords: UPDATE and JOIN)

helios
First para is correct, except that C is updated, with conditions A and B, but that's trivial.
chris
+1  A: 

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();
}
Rob Allen
I like that. Although, what about calling executeNonQuery multiple times? Isn't that an expense as well?
chris
Depends on your query. If you keep it light weight and don't use dynamic SQL then you should be okay. Running the transaction this way uses a single connection which saves a bit of server resources and the query will be fresh in cache.
Rob Allen
Sorry - I meant to say the impact depends on your query. Running anything against the DB is an expense.
Rob Allen
+1  A: 

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"
cmsjr
A: 

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.

Hafthor