【问题标题】:What is the best way to fast insert SQL data and dependant rows?快速插入 SQL 数据和相关行的最佳方法是什么?
【发布时间】:2009-09-06 13:20:16
【问题描述】:

我需要编写一些代码来插入大约 300 万行数据。
同时我需要插入相同数量的伴随行。

即架构如下所示:

Item
  - Id
  - Title

Property
  - Id
  - FK_Item
  - Value

我的第一次尝试是这样的:

BaseDataContext db = new BaseDataContext();
foreach (var value in values)
{
    Item i = new Item() { Title = value["title"]};
    ItemProperty ip = new ItemProperty() { Item = i, Value = value["value"]};
    db.Items.InsertOnSubmit(i);
    db.ItemProperties.InsertOnSubmit(ip);
}
db.SubmitChanges();

显然这非常慢,所以我现在使用这样的东西:

BaseDataContext db = new BaseDataContext();
DataTable dt = new DataTable("Item");
dt.Columns.Add("Title", typeof(string));
foreach (var value in values)
{
    DataRow item = dt.NewRow();
    item["Title"] = value["title"];
    dt.Rows.Add(item);
}
using (System.Data.SqlClient.SqlBulkCopy sb = new System.Data.SqlClient.SqlBulkCopy(db.Connection.ConnectionString))
{
    sb.DestinationTableName = "dbo.Item";
    sb.ColumnMappings.Add(new SqlBulkCopyColumnMapping("Title", "Title"));
    sb.WriteToServer(dt);
}

但这不允许我添加相应的“属性”行。

我认为最好的解决方案可能是添加一个像 this one 这样的存储过程,它通常可以让我进行批量插入(或至少多次插入,但我可能会以某种方式禁用存储过程的日志记录以提高性能)然后返回对应的id。

谁能想到更好的(即更简洁、性能几乎相同)的解决方案?

【问题讨论】:

    标签: c# sql sql-server bulkinsert


    【解决方案1】:

    要结合之前最好的两个答案,并为 ID 添加缺失的部分:

    1) 使用 BCP 将数据加载到这样定义的临时“暂存”表中

    CREATE TABLE stage(Title AS VARCHAR(??), value AS {whatever});
    

    您稍后将需要适当的索引来提高性能:

    CREATE INDEX ix_stage ON stage(Title);
    

    2) 使用 SQL INSERT 加载 Item 表:

    INSERT INTO Item(Title) SELECT Title FROM stage;
    

    3) 最后通过与 Item 加入阶段来加载属性表:

    INSERT INTO Property(FK_ItemID, Value)
    SELECT id, Value
    FROM stage
    JOIN Item ON Item.Title = stage.Title
    

    【讨论】:

    • 当然,但这假设标题是唯一的(它可能是,但我不会依赖它。但是,这可能是最好的方向。
    • 我不得不假设一些事情,因为您没有提供源表的信息。您必须有某种方法来唯一地关联信息:在 SQL 中依赖记录顺序是无效的。
    • 也就是说,您可以在暂存表上放置一个 ID 以进行关联(通常即使与订单相关性也可以使用),但它会变得更加复杂。
    • 是的,我想因为这是一个单一的交易,我可以依靠记录顺序。不过,分配 ID 或依赖唯一标题可能是更好的方法。
    【解决方案2】:

    将这么多数据移入 SQL Server 的最佳方法是 bcp。假设数据从某种文件开始,您需要编写一个小脚本将数据汇集到两个表中。或者,您可以使用 bcp 将数据汇集到一个表中,然后使用 SP 将数据插入到两个表中。

    【讨论】:

    • 问题是,如果第一个表中没有对应的 id,我不能将第二批数据“汇集”到一个表中。我想一种解决方案是分配一个虚假的 id,然后将其删除,但从长远来看,这似乎不是一个很好的方法。
    【解决方案3】:

    将数据批量复制到临时表中,然后调用存储过程,将数据拆分为您需要填充的两个表。

    【讨论】:

    • 谢谢,可能是最好的方法。
    • +1:添加以使用 BCP,除非您有充分的理由不使用(大多数情况下它比 SQLBulkCopy 快 30% 左右),这就是 SOP 答案。
    • 是的,如果可以的话,使用 BCP 而不是批量复制。
    【解决方案4】:

    您也可以使用 .NET SqlBulkCopy 类批量复制代码。

    【讨论】:

    • 我正在使用 .NET SqlBulkCopy 类,你能指出我应该看的地方吗?
    • 如果您可以完全控制数据,也许您可​​以利用构造函数的 SqlBulkCopyOptions 参数的 KeepIdentity 选项。这样,您可以提取当前可用的最高身份值,并从那里开始工作。
    • 谢谢大卫......这只是帮助了我。
    猜你喜欢
    • 2019-05-28
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多