【问题标题】:Multiple DB Updates:多个数据库更新:
【发布时间】:2008-10-10 08:08:39
【问题描述】:

替换问题:Update multiple rows into SQL table

这是用于更新考试结果集的代码片段。 数据库结构是给定的,但我可以提交存储过程以供包含(修改起来很痛苦,所以我把它保存到最后。)

问题:有没有更好的方法使用 SQL server v 2005.,net 2.0 ?

string update = @"UPDATE dbo.STUDENTAnswers 
                              SET ANSWER=@answer
                              WHERE StudentID =@ID and QuestionNum =@qnum";
            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 { }
            }

【问题讨论】:

    标签: c# .net sql-server


    【解决方案1】:

    有几件事很突出:

    • 您没有显示 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、业务逻辑和数据访问层。

    【讨论】:

    • 我认为是否在循环中处理异常应该取决于业务规则。处理循环外的异常意味着您要么停止处理出现错误的项目,要么回滚事务,但在某些情况下,您可能希望跳过错误行并继续处理。
    • 如果您想跳过错误行并继续处理其他行,那么您可能需要捕获 SqlException,并检查 SqlErrors 属性以查找某些特定错误。
    【解决方案2】:

    对于 30 次更新,我认为您在正确的轨道上,尽管关于需要使用 updateCommand 的评论是正确的。

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

    【讨论】:

      【解决方案3】:

      我看到的一个问题是当您打开连接时。

      我至少会在每次更新之前调用打开,然后在更新后关闭连接。

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

      在你需要它之​​前永远不要打开你的命令是一个很好的规则。

      【讨论】:

      • 每次回发循环运行大约 30 次。我的想法是我会保存打开和关闭 30 次。我错了吗?
      • 我同意,无论循环需要执行多久,我宁愿打开连接,也不愿打开和关闭连接 N 次。
      【解决方案4】:

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

      编辑:如果您坚持当前的解决方案,我至少会将您的 SqlConnection 和 SqlCommand 包装在 using 块中,以确保它们被处理掉。

      【讨论】:

        【解决方案5】:

        发出一个针对值表的更新:

        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 StudentID=@ID
        

        所以你只需像这样把它放在一起:

        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 StudentID=@ID");
          updateCommand.CommandText = sb.ToString();
          updateCommand.Parameters.AddWithValue("@ID", uid);
          updateCommand.ExecuteNonQuery();
        }
        

        这具有作为所有其他无操作的优点(例如,如果您在事务中包装了多个更新)并且运行速度更快,因为:

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

        【讨论】:

          猜你喜欢
          • 2022-12-21
          • 2013-12-16
          • 1970-01-01
          • 2021-10-25
          • 1970-01-01
          • 1970-01-01
          • 2023-03-20
          • 2014-08-28
          • 1970-01-01
          相关资源
          最近更新 更多