将记录插入SQL db的更快捷方式

时间:2015-03-23 10:38:39

标签: c# sql sql-server for-loop

我有一个工具,每小时在数据库中插入/更新数千条记录。它将输入读入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();
}

它工作得很好,但速度很慢。你有更快的解决方案吗?

4 个答案:

答案 0 :(得分:1)

使用批量插入SqlBulkCopy ClassTable Valued Parameters in C# 这应该加快速度

答案 1 :(得分:1)

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

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填充了DataTable,则可以使用SqlDataAdapter.Update()方法。如果没有,那么我建议在SQL中处理upsert。由于您使用的是SQL Server 2012,因此可以使用表值参数。第一步是创建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();
}

答案 2 :(得分:0)

这很慢,因为每个插入都是自己的treatet,这会导致事务时间的开销。 尝试使用不同的方法进行批量插入。 无论是在框架上(如弹簧数据批量)还是仅通过更改查询以一次插入多个值。

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

但是不要将它们全部联系起来,不要监督db-connection的缓冲区大小。

答案 3 :(得分:0)

您是否考虑过使用SSIS?或通过Bulk Insert。无论您选择哪种选项,您都希望最大限度地减少进入数据库的连接数 - 一旦数千次调用将会非常密集。