1

编辑:
我的问题不再是问题了:我重做了我的性能测试,我犯了一个致命的愚蠢错误:我忘记了 x1000 从毫秒中获取秒数:/ 对不起那些家伙。
有关信息:
- 我每秒从我的 PC 到本地网络上的数据库服务器进行大约 1900 次更新。
- 如果程序与 DB 在同一台机器上,则每秒更新 3.200 次。
- 每秒 3.500 次从我的 PC 上的数据库服务器更新我不重新创建和重新打开新的 SQLConnection。
- 每秒 5.800 次更新,带有批处理文本。对于我的 10.000 行,如果需要 5 秒,我的程序就可以了。很抱歉让您担心了。

实际上,我使用 SQL 存储过程在我的数据库中创建一行以避免 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 注入风险(而且很丑陋)。

.Net 中是否有管理它的“SqlCommandList”对象?如何在数据库中进行大量写入?那有什么好的模式呢?

4

9 回答 9

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命令。

于 2008-10-14T12:56:30.800 回答
4

SQLDataAdaptor 的 UpdateBatchSize 怎么样?

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

文章

MSDN

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

于 2008-10-14T18:44:11.817 回答
2

Based off Joel's answer, this is the fastest solution short of using either SqlBulkCopy or creating big strings of messy SQL and executing. (I added a transaction which will improve performance quite a lot)

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();
   }
}
于 2008-10-14T13:13:35.353 回答
2

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

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

于 2008-10-14T12:46:24.457 回答
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,您应该保持简单,并按照您现在的方式进行操作。

于 2008-10-14T12:58:35.460 回答
1

我猜这是一个很老的问题。

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

在 SQL 中,您现在可以将所有记录作为单独的项目来处理......实际上使用集合逻辑并获得真正的性能。

于 2011-12-12T18:11:27.863 回答
0

您是否考虑过将 XML 文档传递给存储过程,然后遍历该存储过程以查找要插入的数据?

于 2008-10-14T12:45:38.747 回答
0

“这不是最好的解决方案:我将在网络传输中损失大量时间” 你能忍受损失吗?

如果这是你不经常做的事情,那有关系吗?首先测量它,如果它是一个问题然后修复它,我个人可能会使用 Marc Gravells 表进行传入插入。另一种选择是异步触发插入,然后在开始下一个之前不要等待每个完成。

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

希望这会有所帮助(尽管我认为不会,抱歉)。

于 2008-10-14T12:56:20.053 回答
0

根据上面的一些答案,最显着的性能提升涉及到对现有代码的 2 处更改:

  1. 在事务中包装更新
  2. 仅打开一个连接并使用不同的参数多次调用该过程。

BULK INSERTs 是一种选择,但对于您想要做的事情可能有点矫枉过正。

于 2008-10-14T13:32:54.783 回答