取代问题: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 { }
}
答案 0 :(得分: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 :(得分:2)
对于30次更新,我认为您已走上正轨,但有关使用约updateCommand
的需求的评论是正确的。
我们发现执行批量更新(&gt; 100行)的最佳效果是通过SqlBulkCopy
类到临时表,然后是存储过程调用来填充实时表。
答案 2 :(得分:0)
我看到的问题是您打开连接时的问题。
我至少会在每次更新之前调用open,然后在更新后关闭连接。
如果你的循环需要时间来执行,你的连接将会打开很长时间。
在您需要之前永远不要打开命令是一个很好的规则。
答案 3 :(得分:0)
您可以使用OpenXML批量插入。创建一个包含所有问题和答案的xml文档,并使用它来插入值。
编辑:如果您坚持使用当前的解决方案,我至少会将您的SqlConnection和SqlCommand包装在一个使用块中,以确保它们被处理掉。
答案 4 :(得分:-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 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();
}
这样做的好处是可以进行所有其他操作(例如,如果您在事务中包装了多个更新),并且运行速度更快: