繁体   English   中英

使用Sql插入语句和SqlBulkCopy插入数据有什么区别?

[英]What is the difference between inserting data using Sql insert statements and SqlBulkCopy?

我有向SQL Server插入大量数据的问题。

以前我使用的是Entity Framework,但是对于仅100K根级别的记录(分别包含两个不同的集合,其中每个大约进一步对200K记录进行操作)的速度真是太慢了=内存中的记录约为500K-600K。 在这里,我应用了所有优化(例如AutoDetectChangesEnabled = false,并在每次批处理之后重新创建和布置上下文。)

我拒绝了该解决方案,并使用了非常快且非常高效的BulkInsert。 一分钟左右就可以插入10万条记录。

但是主要的问题是从新插入的记录取回主键。 为此,我正在考虑编写可以在TVP上运行的存储过程(即在存储所有根级100K记录的内存数据表中)。 在里面,我将使用OUTPUT INSERTED.Id来获取应用程序内部的所有主键)。

因此,如何将这种方法(即存储过程中的Sql Insert查询)与SqlBulkCopy方法进行比较。

任何想法,如果我能在SqlBulkCopy操作之后找回所有主键? 或关于OUTPUT Inserted.Id的具体说明将返回应用程序中所有正确的新键。

PS:在此过程中,我不想创建任何登台表。 这只是开销。

这是一个基于注释中讨论的示例/扩展了此处提到的想法: 在SQL BulkCopy之后可以获取PrimayKey ID吗?

  • 从C#批量上传到SQL中的临时表
  • 使用Sql将数据从临时表复制到实际表(此时将生成ID),然后返回ID。

我还没有机会进行测试,但是希望这会有所帮助:

//using System.Data.SqlClient;
//using System.Collections.Generic;

public DataTable CreatePersonDataTable(IEnumerable<PersonDTO> people) 
{
    //define the table
    var table = new DataTable("People");
    table.Columns.Add(new DataColumn("Name", typeof(string)));
    table.Columns.Add(new DataColumn("DOB", typeof(DateTime)));
    //populate it
    foreach (var person in people)
    {
        table.Rows.Add(person.Name, person.DOB);
    }
    return table;
}

readonly string ConnectionString; //set this in the constructor
readonly int BulkUploadPeopleTimeoutSeconds = 600; //default; could override in constructor
public IEnumerable<long> BulkUploadPeople(IEnumerable<PersonDTO> people) //you'd want to break this up a bit; for simplicty I've bunged everything into one big method
{
    var data = CreatePersonDataTable(people);
    using(SqlConnection con = new SqlConnection(ConnectionString)) 
    {
        con.Open(); //keep same connection open throughout session
        RunSqlNonQuery(con, "select top 0 Name, DOB into #People from People");
        BulkUpload(con, data, "#People");
        var results = TransferFromTempToReal(con, "#People", "People", "Name, DOB", "Id");
        RunSqlNonQuery(con, "drop table #People");  //not strictly required since this would be removed when the connection's closed as it's session scoped; but best to keep things clean.
    }
    return results;
}
private void RunSqlNonQuery(SqlConnection con, string sql)
{
    using (SqlCommand command = con.CreateCommand())
    {
        command.CommandText = sql;
        command.ExecuteNonQuery();      
    }
}
private void BulkUpload(SqlConnection con, DataTable data, string targetTable)
{
    using(SqlBulkCopy bulkCopy = new SqlBulkCopy(con))
    {
        bulkCopy.BulkCopyTimeout = 600; //define this in your config 
        bulkCopy.DestinationTableName = targetTable; 
        bulkCopy.WriteToServer(data);         
    }
}
private IEnumerable<long> TransferFromTempToReal(SqlConnection con, string tempTable, string realTable, string columnNames, string idColumnName)
{
    using (SqlCommand command = con.CreateCommand())
    {
        command.CommandText = string.Format("insert into {0} output inserted.{1} select {2} from {3}", realTable, idColumnName, columnNames, tempTable);
        using (SqlDataReader reader = command.ExecuteReader()) 
        {
            while(reader.Read()) 
            {
                yield return r.GetInt64(0);
            }
        }
    }
}

在您的问题中,您已经添加了您不想使用登台表的信息,因为它是“开销”的……请尝试。 您可能会发现,创建登台表的少量开销小于使用此方法所带来的性能提升。

显然,它的速度不会像插入和忽略返回的ID那样快。 但这是您的要求,如果没有其他答案,这可能是最好的选择。

任何想法,如果以某种方式,我可以在SqlBulkCopy操作之后取回所有主键

你不能。 没有办法直接从SqlBulkCopy进行操作。

PS:在此过程中,我不想创建任何登台表。 这只是开销。

不幸的是,如果您想找回主键,则需要这样做或使用其他方法(如您建议的TVP)。

免责声明 :我是Entity Framework Extensions的所有者

一种替代解决方案是使用已经支持BulkInsert for Entity Framework的库。 在后台,它使用SqlBulkCopy + Staging Tables。

默认情况下,BulkInsert方法已经输出主键值。

该库不是免费的,但是,它为您的公司增加了一些灵活性,并且您无需编写任何代码/支持任何内容。

例:

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM