Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Whatis is best approach on executing multiple UPDATE statement in one connection?

How to properly do the following update:

using (OracleConnection conn = new OracleConnection())
using (selCmd)
{

    string sql1 = "update Table1 set name = joe where id = 10;"
    string sql2 = "update Table2 set country = usa where region = americas;"
    string sql3 = "update Table3 set weather = sunny where state = CA;"
    string sql4 = "update Table4 set engine = v8 where maker = benz;"

    cmdUpdate.CommandText = sql(#);
    cmdUpdate.Connection = conn;
    recs = cmdUpdate.ExecuteNonQuery();
}

I am aware of all or nothing if it's a transaction but I just to see how it works with correct approach.

I'm thinking iterate an array of items [sql1,sql2,sql3,sql4] and pass sql(#) in the CommandText and perform ExecuteNonQuery each time.

like image 335
ombinar Avatar asked Nov 28 '12 00:11

ombinar


3 Answers

If I remember correctly, it is possible to concatenate multiple SQL statements in one string separated by semi-colons (;). Otherwise, there is nothing wrong with executing multiple ExecuteNonQuery() calls.

string sql1 = "BEGIN update Table1 set name = 'joe' where id = 10;",
       sql2 = "update Table2 set country = 'usa' where region = 'americas';",
       sql3 = "update Table3 set weather = 'sunny' where state = 'CA';",
       sql4 = "update Table4 set engine = 'v8' where maker = 'benz'; END;";

string sql = string.Format("{0}{1}{2}{3}",sql1,sql2,sql3,sql4);

using (OracleConnection conn = new OracleConnection())
using (OracleCommand cmdUpdate = new OracleCommand(sql, conn))
{
    conn.Open();
    recs = cmdUpdate.ExecuteNonQuery();
}
like image 194
Gerard Sexton Avatar answered Nov 02 '22 05:11

Gerard Sexton


I recently came across this issue in some old code. We dynamically build chain of SQL calls (with support for Oracle and Sql Server). Since there is no current Oracle production implementation, nobody tested Oracle operation and customer bugs are not coming in. I found a code that builds chain of commands and then, for Oracle it uses String.Split(';'). Then, it uses a loop to execute each statement in transaction: rowsAffecter += ExecuteNonQuery....

I don't like this idea because without parameterization it is dangerous approach, since some data can contain ;. But even if parameterization is in place...

... one of the issues of making anonymous block for Oracle ("begin... end;") is that ExecuteNonQuery will not return number of rows (returns -1), which is sometimes needed to judge if something got updated or not.

to solve this issue I've done this

private string AppendOracleCountOrNothing(StringBuilder sql)
{
    if (_myProvider == Providers.Oracle)
        sql.AppendLine("rowCnt := rowCnt + SQL%ROWCOUNT;");
}


public void SomeMethod()
{
    var longSqlChain = new StringBuilder(2000);

    longSqlChain.Append("Insert into table...;");
    AppendOracleCountOrNothing(longSqlChain);
    if (someCondition)
    {
        longSqlChain.AppendLine("Update anotherTable...;");
        AppendOracleCountOrNothing(longSqlChain);
    } 

    // may be, add some more sql to longSqlChain here....

    int rowsAffected;
    if (_myProvider == Providers.Oracle)
    {
        longSqlChain.Insert(0, @"DECLARE
            rowCnt number(10) := 0
            BEGIN
            ").AppendLine(@":1 := rowCnt;
            END;");
        // Now, here we have some abstract wrappers that hide provider specific code. 
        // But the idea is to prepare provider specific output parameter and then parse its value
        IDataParameter p = ParameterWrapper.PrepareParameter(":1", 0, ParameterDirection.Output, myProvider); // note IDataParameter 
        SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, new[]{p});
        rowsAffected = p.GetParameterValue(); // GetParameterValue is an extension on IDataParameter 
    }
    else // sql server
    {
        rowsAffected = SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, null);
    }
}

This way we make one trip to DB and get the return number of rows affected by this call. and queries can be parameterized as well. Again, better to develop abstraction layer, so, you can call something like parameterizer.CreateParameter(10), which will add parameter to collection and generate :1, :2, :3, etc. (oracle) and @1, @2, @3, etc. (sql server), in your sql statement.

like image 2
T.S. Avatar answered Nov 02 '22 05:11

T.S.


Another approach is to create a simple extension method (ExecuteMultipleNonQuery) that simply splits the string on all semicolons and executes each statement in a loop:

public static class DbCommandExtensions {

    public static void ExecuteMultipleNonQuery(this IDbCommand dbCommand)
    {
        var sqlStatementArray = dbCommand.CommandText.Split(new string[] {";"}, StringSplitOptions.RemoveEmptyEntries);
        foreach (string sqlStatement in sqlStatementArray)
        {
            dbCommand.CommandText = sqlStatement;
            dbCommand.ExecuteNonQuery();
        }
    }

}
like image 1
Emil G Avatar answered Nov 02 '22 04:11

Emil G