2008-10-08 110 views
2

取代问题:Update multiple rows into SQL table多个数据库更新:

下面是更新考试结果集的代码片段。 DB结构是给定的,但我可以提交包含存储过程(这是修改痛苦,所以我保存到最后。)

的问题:有没有使用SQL v服务器2005年有更好的方式。 ,净2.0?

string update = @"UPDATE dbo.STUDENTAnswers 
           SET [email protected] 
           WHERE StudentID [email protected] and QuestionNum [email protected]"; 
      SqlCommand updateCommand = new SqlCommand(update, conn); 
      conn.Open(); 

      string uid = Session["uid"].ToString(); 
      for (int i= tempStart; i <= tempEnd; i++) 
      { 
       updateCommand.Parameters.Clear(); 
       updateCommand.Parameters.AddWithValue("@ID",uid); 
       updateCommand.Parameters.AddWithValue("@qnum",i); 
       updateCommand.Parameters.AddWithValue("@answer", Request.Form[i.ToString()]); 
       try 
       { 
        updateCommand.ExecuteNonQuery(); 
       } 
       catch { } 
      } 

回答

4

有几件事情中脱颖而出:

  • 你不显示在SqlConnection的实例化,因此目前还不清楚,你妥善处理它。

  • 您不应该在循环中吞食异常 - 更好地在顶级异常处理程序中处理它们。

  • 您正在通过循环实例化每次迭代的新参数 - 您可以重新使用这些参数。

把此一并它可能看起来像下面的(如果你不想使用事务,即不关心,如果一些但不是所有的更新成功):

using (SqlConnection conn = new SqlConnection(connectionString)) 
{ 
    conn.Open(); 
    using (SqlCommand updateCommand = new SqlCommand(update, conn)) 
    { 
     string uid = Session["uid"].ToString(); 
     updateCommand.Parameters.AddWithValue("@ID", uid); 
     updateCommand.Parameters.AddWithValue("@qnum", i); 
     updateCommand.Parameters.Add("@answer", System.Data.SqlDbType.VarChar); 
     for (int i = tempStart; i <= tempEnd; i++) 
     { 
      updateCommand.Parameters["@answer"] = Request.Form[i.ToString()]; 
      updateCommand.ExecuteNonQuery(); 
     } 
    } 
} 

或使用事务,以确保全有或全无:

using (SqlConnection conn = new SqlConnection(connectionString)) 
{ 
    conn.Open(); 
    using (SqlTransaction transaction = conn.BeginTransaction()) 
    { 
     using (SqlCommand updateCommand = new SqlCommand(update, conn, transaction)) 
     { 
      string uid = Session["uid"].ToString(); 
      updateCommand.Parameters.AddWithValue("@ID", uid); 
      updateCommand.Parameters.AddWithValue("@qnum", i); 
      updateCommand.Parameters.Add("@answer", System.Data.SqlDbType.VarChar); 
      for (int i = tempStart; i <= tempEnd; i++) 
      { 
       updateCommand.Parameters["@answer"] = Request.Form[i.ToString()]; 
       updateCommand.ExecuteNonQuery(); 
      } 
      transaction.Commit(); 
     } 
    } // Transaction will be disposed and rolled back here if an exception is thrown 
} 

最后,另一个问题是,你是混合数据访问代码UI代码(例如的Request.Form)。分离这些将会更模块化和可测试 - 例如通过将应用程序分解为UI,业务逻辑和数据访问层。

-1

发出一个更新是违背了值表:

UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN (
    SELECT 1 as q, 'answer1' as a 
    UNION ALL SELECT 2, 'answer2' -- etc... 
) x ON s.QuestionNum=x.q AND [email protected] 

所以你只要把这个在一起,就像这样:

using(SqlCommand updateCommand = new SqlCommand()) { 
    updateCommand.CommandType = CommandType.Text; 
    updateCommand.Connection = conn; 
    if (cn.State != ConnectionState.Open) conn.Open(); 

    StringBuilder sb = new StringBuilder("UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN ("); 
    string fmt = "SELECT {0} as q, @A{0} as a"; 
    for(int i=tempStart; i<tempEnd; i++) { 
    sb.AppendFormat(fmt, i); 
    fmt=" UNION ALL SELECT {0},@A{0}"; 
    updateCommand.Parameters.AddWithValue("@A"+i.ToString(), Request.Form[i.ToString()]); 
    } 
    sb.Append(") x ON s.QuestionNum=x.q AND [email protected]"); 
    updateCommand.CommandText = sb.ToString(); 
    updateCommand.Parameters.AddWithValue("@ID", uid); 
    updateCommand.ExecuteNonQuery(); 
} 

这具有的其他所有优点没有任何操作(如果你在交易中包装了几个更新)并且运行速度会更快:

  • 表和相关的索引看着/更新一次
  • 只需支付应用程序与数据库服务器之间的延迟一次,而不是每次更新
0

我看到的一个问题是,当你正在打开您的连接。

我会至少在每次更新之前调用open并在更新后关闭连接。

如果您的循环需要时间来执行,您的连接将会打开很长时间。

这是一个很好的规则,永远不要打开你的命令,直到你需要它。

+0

每次回发循环运行约30次。 我的想法是,我可以节省30次开闭。我错了吗? – 2008-10-08 19:49:16

+0

我同意,我宁愿有一个打开的连接,无论循环需要执行多长时间,都要打开和关闭连接N次。 – palmsey 2008-10-08 20:45:56

0

您可以使用OpenXML进行批量插入。创建一个包含所有问题和答案的XML文档,并使用它来插入值。

编辑:如果您坚持使用您当前的解决方案,我至少会将您的SqlConnection和SqlCommand包装在一个使用块中,以确保它们被丢弃。

2

对于30个更新,我认为你是在正确的轨道上,虽然关于需要在updateCommand附近使用的评论是正确的。

我们发现执行批量更新(> 100行)的最佳执行方式是通过SqlBulkCopy类转移到临时表,然后通过存储过程调用来填充活动表。