C#-SQL:如何执行一批StoredProcedure?

时间:2008-10-14 12:40:37

标签: c# performance stored-procedures batch-file sqlcommand

编辑:
我的问题不再是问题了:我重做了我的表演测试并且我做了一个致命的愚蠢错误:我忘记了x1000从毫秒获得秒数:/ 对不起那些家伙。
有关信息:
    - 我从PC到本地网络上的DataBase服务器每秒进行1900次更新     - 如果程序与DB相同,则每秒3.200次更新     - 我的PC上DataBase服务器每秒3.500次更新我不会重新创建并重新打开一个新的SQLConnection。
    - 批量文本每秒5.800次更新。 对于我的10.000行,如果需要5秒钟,我的程序就可以了。很抱歉让您担心。

实际上,我使用SQL存储的prodedure在我的数据库中创建一行以避免SQL注入。在C#中,我有以下方法:

public void InsertUser(string userCode)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      sqlCommand.Parameters.Add(new SqlParameter("@UserCode", userCode));
      sqlConnection.Open();
      sqlCommand.ExecuteNonQuery();///0.2 seconds !ERROR HERE! 0.2ms here,NOT 0.2sec!!!
   }
} 

当我要插入一行或两行时,它会很棒。但如果我需要创建1.000个用户和10.000个产品以及5000个宠物,那么这不是最佳解决方案:我将在网络传输中浪费大量时间。

我相信,如果没有签入,我只能使用有限数量的回调。所以我不想打电话10.000次:

sqlCommand.BeginExecuteNonQuery()

另一种方法是创建批处理文本,但存在SQL注入风险(并且很难看)。

是否有一个'SqlCommandList'对象可以在.Net中管理它?如何在数据库中进行大量写作?对此有什么好处?

9 个答案:

答案 0 :(得分:10)

这应该更快一点:

public void InsertUser(IEnumerable<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString), 
             SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection))
   {
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);
      sqlConnection.Open();

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();///0.2 seconds
      }
   }
}

即使你传递了1000个用户,它也只会打开一个连接而只创建一个命令。不过,它仍将分别执行每个插入操作。当然,如果userCode不是一个字符串,你需要适当地重新考虑它。您可能还想查看SQL Server的BULK INSERT命令。

答案 1 :(得分:3)

SQLDataAdaptor的UpdateBatchSize怎么样?

我们的前端人员使用它将几个10,000个proc调用批处理为块

Article

MSDN

我们的环境不允许“bulkadmin”权限,所以我们不能使用BULKINSERT / bcp等

答案 2 :(得分:2)

就个人而言,如果我经常期望进行相当大的插入(10,000行肯定会有资格......),我可能会考虑为传入数据设置一个单独的表,并使用SqlBulkCopy来填充此表。然后,您只需执行一个将数据移动到真实表中的存储过程。

另一种方法是将xml发送到数据库,并使用sqlxml来解析它(使用SQL2005及更高版本更容易) - 但这会在数据库服务器上进行额外的工作。

答案 3 :(得分:2)

如果你真的关心这个问题,你可以(就像你说的那样)按照这样的字符串批处理命令:

var cmd = new SqlCommand();
cmd.Connection = sqlConnection;
for (int i = 0; i < batchSize; i++) {
    cmd.CommandText += String.Format("EXEC InsertUser @UserCode{0};", i);
    cmd.Parameters.AddWithValue("@UserCode" + i.ToString(), XXXXX);
    //... etc ...
}

因为在此方案中,您将使用参数,所以与使用存储过程相比,您没有更多的SQL注入风险。但我怀疑你是否真的会节省相当多的时间。 IMO你应该保持简单,就像你现在这样做。

答案 4 :(得分:2)

根据Joel的回答,这是使用SqlBulkCopy或创建大字符串乱码SQL并执行的最快解决方案。 (我添加了一个可以提高性能的交易)

public void InsertUser(IEnumerabler<string> userCodes)
{
   using (SqlConnection sqlConnection = new SqlConnection(this.connectionString))
   {
      sqlConnection.Open();
      SqlTransaction transaction = connection.BeginTransaction();
      SqlCommand sqlCommand = new SqlCommand("InsertUser", sqlConnection);
      sqlCommand.Transaction = transaction;
      sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
      SqlParameter param = sqlCommand.Parameters.Add("@UserCode", SqlDbTypes.VarChar);

      foreach(string code in userCodes)
      {
          param.Value = code;
          sqlCommand.ExecuteNonQuery();
      }      
      transaction.Commit();
   }
}

答案 5 :(得分:1)

我猜这是一个非常古老的问题。

使用SQL Server 2008,现在的答案是使用表值参数。简而言之,将所有变量传入已使用的已定义类型(表)。

在SQL中,您现在可以将所有记录作为单个项目进行处理......实际上使用设置逻辑并获得真实的性能。

答案 6 :(得分:0)

您是否考虑过将XML文档传递给存储过程,然后通过迭代来查找要插入的数据?

答案 7 :(得分:0)

“这不是最好的解决方案:我将在netwok运输中浪费大量时间” 你能忍受这种损失吗?

如果这是你不经常做的事情,那么它是否重要? 首先测量它,如果它是一个问题然后解决它,个人可能我会去Marc Gravells表进行插入。 另一个选择是异步触发插入,然后你不会等到每个插件完成后再开始。

花了我几年时间,但最后我发现我不应该浪费时间来优化不需要它的代码。

希望这会有所帮助(即使我认为不会,对不起)。

答案 8 :(得分:0)

根据上面的一些答案,最少努力的最明显的性能提升涉及对现有代码的2次更改:

  1. 在事务中包装更新
  2. 只打开一个连接,并使用不同的参数多次调用该过程。
  3. BULK INSERT是一个选项,但对于你想做的事情可能有些过分。