【问题标题】:SQL Bulk insert with parent/child relationships, is order preserved?具有父/子关系的 SQL 批量插入,是否保留顺序?
【发布时间】:2011-09-20 11:39:46
【问题描述】:

与下面提到的其他问题类似,我有两个表的结构:

create table parent (
   recno int identity(1,1) primary key not null,
   groupCode int,
   parentdata varchar(80)
);

create table child (
   parentrecno int not null,
   childdata varchar(80)
)

我需要在这些表中快速插入几十万条记录——这些表包含数百万条与此插入无关的其他记录,并且从不安静。由于父母/孩子的性质,它不是SqlBulkCopy 的好候选人(似乎)。

在 C# 中使用 SqlCommandINSERT 我每秒插入大约 400-500 条记录,这有点太慢了。伪代码:

 foreach(Record r in parentRecords)
 {
      Insert Fields from r into SqlCommand Parameters but not "recno"
      Call ExecuteScalar to insert and fetch the inserted identity value (recno)
      foreach(ChildRecord cr in parentRecords.Children)
      {
          Insert Fields from cr into SqlCommand Parameters
          Insert the identity value (recno) from above into Parameters 
                                                       (as parentrecno)
          Call ExecuteNonQuery to insert the record
      }   
 }

阅读其他帖子后,我想到了一个想法。附加到父记录的groupCode 对于我要插入的父记录集是唯一的。是否可以:

  1. 使用SqlBulkCopy 批量插入父记录,让插入像往常一样自动生成recno 标识字段。
  2. 只对插入的记录执行SELECT

    select recno from parent where groupCode = @thisgroup order by recno;
    
  3. 使用检索到的值填写内存中子记录的parentrecno字段

  4. 使用SqlBulkCopy批量插入子记录

这将依赖于以与原始 DataTable 中相同的顺序进入 SQL 表的父记录(并且以相同的顺序分配标识值)。 这是我可以依赖的吗?

相关问题:

How to update Dataset Parent & Child tables with Autogenerated Identity Key?

SqlBulkCopy and DataTables with Parent/Child Relation on Identity Column

【问题讨论】:

    标签: c# sql-server-2008 sqlbulkcopy


    【解决方案1】:

    创建两个与目标表结构相同但不在 recno 列上使用标识的临时表。

    create table parentTmp (
       recno int,
       groupCode int,
       parentdata varchar(80)
    );
    
    create table childTmp (
       parentrecno int not null,
       childdata varchar(80)
    )
    

    将您的数据批量加载到临时表,保持 recno/parentrecno 值不变。

    然后您可以使用mergeoutput 从临时表中移动数据。

    -- Table variable to hold mapping between 
    -- SourceRecno and TargetRecno
    declare @recno table(SourceRecno int, TargetRecno int);
    
    -- Merge data from parentTmp to parent
    -- Output old and new recno to @recno
    merge parent T
    using parentTmp S
    on 0=1
    when not matched then
      insert (groupCode, parentdata)
        values (S.groupCode, S.parentData)
    output S.recno, inserted.recno into @recno;
    
    -- Copy data from childTmp to child
    -- Use @recno to get the new recno
    insert into child(parentrecno, childdata)
    select R.TargetRecno, C.childdata
    from childTmp as C
      inner join @recno as R
        on C.parentrecno = R.SourceRecno;
    

    这仅适用于 SQL Server 2008(我想是以后的)。

    【讨论】:

    • 这是一段有趣的代码,它使用了我不熟悉的语句。这听起来是有道理的。 :) 给我一点去了解它。
    • @clintp 您可以查看此问题以获取更多信息。 stackoverflow.com/questions/5365629/…
    • 有没有办法在 SQL 2005 中执行相同的语句。我试图找到 2005 的合并使用适应,但我无法访问源表的 ID 或在目标表中插入 ID。除了您的声明:您应该考虑在内连接上使用option(recompile)mssqltips.com/sqlservertip/2140/…
    【解决方案2】:

    这不是绝对的批量插入,而是将所有子数据与父数据同时插入,只对数据库进行 1 次往返。

    insert into parent(groupcode, parentdata) values(1, 'parent data');
    insert into child(parentrecno, childdata) select parentrecno, childdata from (
        select SCOPE_IDENTITY() as parentrecno, 'child data 1' as childdata
        union
        select SCOPE_IDENTITY() as parentrecno, 'child data 2' as childdata
        union
        select SCOPE_IDENTITY() as parentrecno, 'child data 3' as childdata
    ) childrendata;
    

    您可以在 C# 代码中构建这样的脚本,然后为每个父级执行一个请求。

    请注意,如果已知子数据量很大,这可能不是一个好方法。不知道细节,但我确定sql脚本的大小不能无限增长。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-14
      • 2019-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-25
      • 1970-01-01
      相关资源
      最近更新 更多