5k+ SQL 插入需要很长时间 - 我该如何改进它?

5k+ SQL inserts takes a long time - how can I improve it?

通常我只运行这个循环少于50并且它运行没问题。现在我正在尝试扩大规模,5k+ 需要几个分钟。

Public Shared Sub Add(type As BufferType, objIDs As List(Of Integer), userToken As String)
    Dim timeInUTCSeconds As Integer = Misc.UTCDateToSeconds(Date.Now)
    For Each objID As Integer In objIDs
        Dim insertStmt As String = "IF NOT EXISTS (SELECT ObjectID From " & TableName(type) & " where ObjectID = " & objID & " and UserToken = '" & userToken.ToString & "')" & _
        " BEGIN INSERT INTO " & TableName(type) & "(ObjectID,UserToken,time) values(" & objID & ", '" & userToken.ToString & "', " & timeInUTCSeconds & ") END" & _
        " ELSE BEGIN UPDATE " & TableName(type) & " set Time = " & timeInUTCSeconds & " where ObjectID = " & objID & " and UserToken = '" & userToken.ToString & "' END"
        DAL.SQL.Insert(insertStmt)
    Next
End Sub

最好的选择是将整个工作移到 SQL 一边。将您的 objIDs 列表作为 table 参数传递给存储过程并使用 MERGE..UPDATE..INSERT 语句。

如果您需要 VB 副代码,您可以进行一些优化。

  • SQL 参数。必须做。
  • 检查 DAL.SQL class 是否正确处理连接。
  • 从命令构建单个批次并运行它作为整个批次。

(考虑到这是一个 .Net 问题,我假设 SQL 服务器的合理最新版本。)

  1. 如果可能的话,将其移动到一个存储过程中,该过程采用 table-valued 参数或带分隔符的 ID 字符串。
  2. 参数化您的查询。当 DBMS 在执行一串没有参数的代码时,它无法计算出哪些位是常量,哪些位是参数——所以它每次都会计算出一个新的执行计划。使用数据参数,它可以重用执行计划并且 运行 更快。
  3. 对于非常大的重复性工作,连接延迟可能会成为一个非常大的问题。如果您 真的 必须从调用应用程序执行类似 ad-hoc SQL 的操作(我不会这样做,但这是您的项目),请尝试批处理语句并一起执行它们,而不是发出 5000 个单独的连接。应该明显更快。

您应该始终使用 SQLBULKCOPY for inserting a large amount of data. You can have a look at here,它一直被 认为是 作为将大量数据插入 table 的最佳实践。

一个演示代码将使您清楚地了解事实,该演示代码摘自 here

private static void PerformBulkCopy()
{
    string connectionString =
            @"Server=localhost;Database=Northwind;Trusted_Connection=true";
    // get the source data
    using (SqlConnection sourceConnection =
            new SqlConnection(connectionString))
    {
        SqlCommand myCommand =
            new SqlCommand("SELECT * FROM tablename", sourceConnection);
        sourceConnection.Open();
        SqlDataReader reader = myCommand.ExecuteReader();

        // open the destination data
        using (SqlConnection destinationConnection =
                    new SqlConnection(connectionString))
        {
            // open the connection
            destinationConnection.Open();

            using (SqlBulkCopy bulkCopy =
            new SqlBulkCopy(destinationConnection.ConnectionString))
            {
                bulkCopy.BatchSize = 500;
                bulkCopy.NotifyAfter = 1000;
                bulkCopy.SqlRowsCopied +=
                    new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
                bulkCopy.DestinationTableName = "Tablename";
                bulkCopy.WriteToServer(reader);
            }
        }
        reader.Close();
    }
}

另外不要忘记阅读与 Batchsize

相关的文档

希望对您有所帮助。