Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/284.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Sql insert语句插入数据与使用SqlBulkCopy插入数据有什么区别?_C#_Sql Server_Entity Framework_Bulkinsert_Table Valued Parameters - Fatal编程技术网

C# 使用Sql insert语句插入数据与使用SqlBulkCopy插入数据有什么区别?

C# 使用Sql insert语句插入数据与使用SqlBulkCopy插入数据有什么区别?,c#,sql-server,entity-framework,bulkinsert,table-valued-parameters,C#,Sql Server,Entity Framework,Bulkinsert,Table Valued Parameters,我在向SQL server插入大量数据时遇到问题 以前我使用的是实体框架,但对于仅包含两个不同集合的100K根级记录来说,速度非常慢,其中每个集合进一步操作200K记录,大约=内存中的500K-600K记录。 在这里,我应用了所有优化,例如AutoDetectChangesEnabled=false,并在每个批处理之后重新创建和处理上下文 我拒绝了这个解决方案,并使用了BulkInsert,它的速度非常快,效率也非常高。在一分钟左右的时间里插入了10万条记录 但是主要的问题是从新插入的记录中取回

我在向SQL server插入大量数据时遇到问题

以前我使用的是实体框架,但对于仅包含两个不同集合的100K根级记录来说,速度非常慢,其中每个集合进一步操作200K记录,大约=内存中的500K-600K记录。 在这里,我应用了所有优化,例如AutoDetectChangesEnabled=false,并在每个批处理之后重新创建和处理上下文

我拒绝了这个解决方案,并使用了BulkInsert,它的速度非常快,效率也非常高。在一分钟左右的时间里插入了10万条记录

但是主要的问题是从新插入的记录中取回主键。为此,我想写一个存储过程,它可以在TVP上运行,即内存中的数据表中保存所有根级别的100K记录。 在那个里,我将使用OUTPUT INSERTED.Id来获取应用程序中的所有主键

那么,我如何比较这种方法,即存储过程内的Sql插入查询和SqlBulkCopy方法呢

不知怎的,我可以在SqlBulkCopy操作后取回所有主键吗?或者关于插入的输出的具体内容。Id将在应用程序中返回所有正确的新键


PS:我不想在此过程中创建任何临时表。这只是一项开销。

以下是一个基于评论中讨论/扩展此处提及想法的示例:

i、 e

在SQL中执行从C到临时表的批量上载 使用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那样快;但如果这是您的要求,在没有其他答案的情况下,这可能是最好的选择。

以下是一个基于评论中讨论/扩展此处提及想法的示例:

i、 e

在SQL中执行从C到临时表的批量上载 使用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

免责声明:我是

另一种解决方案是使用已经支持BulkInsert for Entity Framework的库。在后台,它使用SqlBulkCopy+临时表

默认情况下,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;
});
不知怎的,我可以在SqlBulkCopy之后取回所有主键吗 操作

你不能。无法直接从SqlBulkCopy执行此操作

PS:我不想在此过程中创建任何临时表。这 这只是一笔开销

不幸的是,如果你想拿回你的主键,你需要这样做,或者像你建议的那样使用另一种方法TVP

免责声明:我是

另一种解决方案是使用已经支持BulkInsert for Entity Framework的库。在后台,它使用SqlBulkCopy+临时表

默认情况下,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;
});

@JohnLBevan的可能副本:我不想创建任何暂存表,从那里我可以插入到最终表。那是错误的想法。我已经通过了TVP,它与目标表的数据和结构完全相同。@Usman不使用暂存表通常是非常非常糟糕的
主意在ETL中使用EF或任何ORM更糟糕。ETL场景没有业务功能,只有数据转换。@Usman;TVP表值参数与数据库本身中的内容不同。在上面的链接上尝试建议的答案,看看它是否对你有好处;如果没有,请让我们知道。如果你的题目和问题的主体都问同样的问题,那就太好了。在标题中,您似乎想了解两者之间的内部实现差异。在正文中,您似乎特别需要从SQL BulkCopy获取ID—这确实是链接问题的精确副本。那么-这是哪一个?可能是@JohnLBevan的重复:我不想创建任何暂存表,从那里我可以插入到最终表。那是错误的想法。我已经通过了TVP,它与目标表的数据和结构完全相同。@Usman不使用暂存表通常是一个非常非常糟糕的主意。在ETL中使用EF或任何ORM更糟糕。ETL场景没有业务功能,只有数据转换。@Usman;TVP表值参数与数据库本身中的内容不同。在上面的链接上尝试建议的答案,看看它是否对你有好处;如果没有,请让我们知道。如果你的题目和问题的主体都问同样的问题,那就太好了。在标题中,您似乎想了解两者之间的内部实现差异。在正文中,您似乎特别需要从SQL BulkCopy获取ID—这确实是链接问题的精确副本。那么,是哪一个呢?