将记录插入 SQL 数据库的更快方法

Quicker way to insert records to SQL db

我有一个工具可以在数据库中每小时 inserts/updates 数千条记录。它将输入读入 dataTable 并逐行运行 SQL 命令:

for(int i = 0; i < dataTable.Rows.Count; i++)
{
    string sqlConnectionString = "server, db, user, pass, etc.";
    SqlConnection sqlDBConnection = new SqlConnection(sqlConnectionString);

    string query = @"INSERT INTO table 
                    (col1, col2, col3, etc.) 
                    VALUES 
                    (@col1, @col2, @col3, etc.)";
    SqlCommand queryCommand = new SqlCommand(query, sqlDBConnection);

    queryCommand.Parameters.Add("@col1", SqlDbType.Int);
    queryCommand.Parameters["@col1"].Value = Convert.ToInt32(dataTable.Rows[i][0]);

    queryCommand.Parameters.Add("@col2", SqlDbType.VarChar);
    queryCommand.Parameters["@col2"].Value = dataTable.Rows[i][1].ToString();

    queryCommand.Parameters.Add("@col3", SqlDbType.VarChar);
    queryCommand.Parameters["@col3"].Value = dataTable.Rows[i][2].ToString();

    sqlDBConnection.Open();
    queryCommand.ExecuteNonQuery();
    sqlDBConnection.Close();
}

它运行良好,但速度很慢。您有更快的解决方案吗?

使用带有 SqlBulkCopy Class or Table Valued Parameters in C# 的批量插入。
这应该会加快速度

这很慢,因为每个插入都是单独处理的,这会导致事务时间的开销。 尝试使用不同的方法进行批量插入。 无论是在框架上(如 spring-data bulk)还是仅仅通过更改查询以一次性插入多个值。

insert into table (col1, col2, col3, ...)
values            (val1, val2, cal3, ...),
                  (val1, val2, cal3, ...),
                  (val1, val2, cal3, ...), ...

但不要将它们全部连接起来以免超过数据库连接的缓冲区大小。

由于您的数据已经在 DataTable 中,我认为最好的方法是使用 SQLBulkCopy。例如

string sqlConnectionString = "server, db, user, pass, etc.";
using (var bulkCopy = new SqlBulkCopy(sqlConnectionString))
{
    bulkCopy.DestinationTableName = "table";
    bulkCopy.ColumnMappings.Add("Col1", "Col1");
    bulkCopy.ColumnMappings.Add("Col2", "Col2");
    bulkCopy.ColumnMappings.Add("Col3", "Col3");
    bulkCopy.WriteToServer(dataTable);
}

要更新现有记录,如果您已使用 SqlDataAdapter 填充数据表,则可以使用 SqlDataAdapter.Update() method. If not, then I would recommend handling the upsert in SQL. Since you are using SQL Server 2012 you can use Table Valued Parameter. The first step is to create your Table type

CREATE TYPE dbo.YourTableType TABLE
(
    Col1 INT,
    Col2 INT,
    Col3 INT
);

这应该与您的 c# DataTable 具有相同的定义。

然后下一步将创建一个存储过程,接受此类型作为参数,并根据是否存在匹配使用 MERGE 插入或更新记录:

CREATE PROCEDURE dbo.UpsertYourTable @Table dbo.YourTableType READONLY
AS
BEGIN
    MERGE dbo.YourTable WITH (HOLDLOCK) AS t
    USING @Table AS s
        ON s.Col1 = t.Col1 -- OR HOWEVER YOU IDENTIFY EXISTING RECORDS
    WHEN MATCHED THEN UPDATE
        SET Col2 = Col2,
            Col3 = Col4
    WHEN NOT MATCHED THEN 
        INSERT (Col1, Col2, Col3, Col4)
        VALUES (s.Col1, s.Col2, s.Col3, 'Test');

END;
GO

最后,要从 c# 中调用它,您可以使用:

string sqlConnectionString = "server, db, user, pass, etc.";
using (var connection = new SqlConnection(sqlConnectionString))
using (var command = new SqlCommand("dbo.UpsertYourTable", connection))
{
    command.CommandType = CommandType.StoredProcedure;
    var tvp = new SqlParameter("@Table", SqlDbType.Structured);
    tvp.Value = dataTable;
    tvp.TypeName = "dbo.YourTableType";
    command.Parameters.Add(tvp);
    command.ExecuteNonQuery();
}

您是否考虑过使用 SSIS?或通过 Bulk Insert。无论您选择什么选项,您都希望最大程度地减少连接到数据库的连接数——调用一次数千次将是密集的。