我必须更新包含数百万条记录的表格。现在我存储要在List中更新的所有记录的id。该查询以实用方式生成,如下所示:
string queryPart="";
foreach (int id in transactionsToUpdate.ToList())
{
queryPart+="TransactionID="+id;
queryPart+=" OR ";
}
queryPart += "1=0";
string query = @"UPDATE dbo.OutgoingQueue SET Status='C' WHERE "+queryPart;
目前即使列表中有100,000个值,也会出现两个问题。首先,上面的代码需要很长时间才能执行(查询形成部分)。其次,当我在DB上执行查询时,它会给出Timeout Expired异常。是否有更好的方法来实现我想要的目标?
更新: 使用stringbuilder解决了查询需要很长时间才能形成的第一个问题。但第二个问题仍然存在。如果我增加超时,那么我会从资源异常中获取sql。
答案 0 :(得分:2)
这是表值参数的理想用例。见这里:http://msdn.microsoft.com/en-us/library/bb675163.aspx
或者,你也可以创建一个#temp表(或一个临时表),用SqlBulkCopy
(see here)填充它,然后用JOIN
填充它UPDATE
来执行你的{{ 1}}。
答案 1 :(得分:1)
您可以找到一种方法将100,000个值传递到数据库中,尽管您使用的参数很快就会运行到极限。
更新其他包含在交易
中或者这是准备好的查询用于
的内容using (var conn = <GETCONNETIONMETHOD>)
{
conn.Open();
using (var tran = conn.BeginTransaction())
{
using (var cmd = conn.CreateCommand(
@"update dbo.outgoingqueue set status = 'C' where transactionID = @id"))
{
cmd.Transaction = conn.BeginTransaction();
var param = cmd.Parameters.Add("@id", typeof(int));
cmd.Prepare();
foreach (int id in transactionsToUpdate.ToList())
{
param.Value = id;
cmd.ExecuteNonQuery();
}
tran.Commit();
}
}
}
如果您有足够的权限来执行批量复制,那么最好的方法是
using (var conn = <GETCONNECTIONMETHOD>)
{
var dt = new DataTable;
dt.BeginLoadData();
dt.Columns.Add("id");
foreach (int id in transactionsToUpdate.ToList() {
dt.Rows.Add(id);
}
dt.EndLoadData();
using (var cmdSetup = conn.CreateCommand(@"create table #tempUpdate(int id)")) {
cmdSetup.ExecuteNonQuery();
}
var bcp = new SqlBulkCopy(conn);
bcp.DestinationTableName = "#tempUpdate";
bcp.WriteToServer(dt);
using (var cmdUpdate = conn.CreateCommand(
@"update o set status = 'C' from dbo.outgoingQueue o " +
@"inner join #tempUpdate t on o.transactionId = t.id"))
{
cmd.ExecuteNonQuery();
}
}
答案 2 :(得分:0)
你可以做以下事情来解决你的问题。
速度问题 为此,让我知道transactionsToUpdate.ToList()这个列表中的项目是什么?如果你 从数据库填写此列表然后我建议修改您的更新查询,以便您不需要运行for循环。它将提高您的应用程序的性能。你可以做以下的查询。不需要每次都使用循环。它会比你当前的代码运行得快得多,我认为如果你使用这个查询,你将不会面临超时问题。
UPDATE dbo.OutgoingQueue
SET Status='C'
FROM dbo.OutgoingQueuE AS A
INNER JOIN
(
QUERY BY WHICH YOU FILL UP CURRENT LIST
) AS B ON A.TransactionID = B.ID
超时问题 您可以在执行sql命令时设置命令超时。
SqlCommand myCommand = new SqlCommand(); myCommand.CommandTimeout = 15;
答案 3 :(得分:0)
假设transactionsToUpdate是一个int的列表,
在此处获取以逗号分隔的列表:
string queryPart = String.Join(",", transactionsToUpdate.ToArray());
然后在查询中传递它:
string query = @"UPDATE dbo.OutgoingQueue SET Status='C' WHERE TransactionID IN(" + queryPart + ")";
或者您可以创建一个接受逗号分隔的值列表的存储过程,并将queryPart传递给存储过程。
<强>更新强>
然后,您可以通过.Net进行批量操作,例如:
int count = 0;
int bulkCount = 1000;
while (count < transactionsToUpdate.Count)
{
string queryPart = String.Join(",", transactionsToUpdate.ToArray().Skip(count).Take(bulkCount));
string query = @"UPDATE dbo.OutgoingQueue SET Status='C' WHERE TransactionID IN(" + queryPart + ")";
//execute the sql here by doing the ExecuteNonQuery call.
count += bulkCount;
}
此查询将从列表中取出前1000个,处理它们,然后再处理1000个,直到所有这些处理完毕。
答案 4 :(得分:0)
有关更新大小的答案:
如果要更新的id值很小,那么您可以创建批处理语句并在单个事务中发送它。
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %1;
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %2;
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %3;
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %4;
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %5;
update dbo.outgoingqueue out set status = 'C' where out.transactionID = %6;
如果在单个事务中id不是那么小,你可以创建一个临时表并执行这样的更新查询:
update dbo.outgoingqueue out set status = 'C' where
exists (select null from tmp_tab where tmp_tab.transactionID = out.transactionID);
如果您计划更新所有记录,最好的方法是根本不更新。
您应该使用以下命令创建一个包含新名称的新表:
select <column list> into <table name> from <source>;
然后在select
中设置您的新值,最后您只需重命名表格。
答案 5 :(得分:0)
我会采取的措施:
cmd.CommandText = "CREATE TABLE #Values( id Int )";
cmd.ExecuteNonQuery();
foreach (int id in transactionsToUpdate.ToList())
{
cmd.CommandText = "INSERT INTO #Values VALUES( " + id.ToString() + ");"
cmd.ExecuteNonQuery();
}
现在,您可以通过加入此TEMP表(SQL的自然域)来测试该值,而不是100,000个IF。如果您要对同一组数字进行多次测试,那么在加载表后对其进行索引可能是有意义的。
答案 6 :(得分:0)
您的代码的一个建议
使用StringBuilder
代替使用String
它加快了你的过程
StringBuilder queryPart = new StringBuilder("");
foreach (int id in transactionsToUpdate.ToList())
{
queryPart.Append("TransactionID=");
queryPart.Append(id);
queryPart.Append(" OR ");
}
queryPart.Append("1=0");
string query = @"UPDATE dbo.OutgoingQueue SET Status='C' WHERE "+queryPart.toString();
如果您正在进行大型操作,则应始终使用StringBuileder
编辑1 您可以使用StopWatch类检查执行性能
这将显示StringBuilder的速度比String
快100到1000倍