【问题标题】:Fast insert into parent and child tables using C# and Npgsql使用 C# 和 Npgsql 快速插入父表和子表
【发布时间】:2017-06-16 05:55:06
【问题描述】:

我正在使用 C#.NET 4.0 (Visual Studio 2010)、PostgreSQL 9.2 和 Npgsql 2.0.12。我无法升级到 Npgsql 3。

我需要快速插入父表,然后使用该插入的主键快速插入子表。

父表有一个定义为“串行”的列,它是主键。

子表有一个整数列,它是返回父表的外键。

并非每个父记录都会有子记录。一个父级可以有 0 个、1 个或多个子级。

目前我正在将父对象缓冲到列表中。当缓冲了 5000 个父级时,从线程池中生成一个新线程以将记录写入数据库。 (创建了一个新列表 让主线程缓冲下一组父对象。) 新线程调用 NpgsqlConnection.BeginTransaction(),然后在循环内调用 NpgsqlCommand.ExecuteScalar() 并带参数插入父记录并取回主键。 然后构建父对象的子对象(如果有)并保存到另一个列表。在循环结束时提交父母的交易。但是这种方法非常缓慢。插入 5000 条记录需要 3 到 10 秒。肯定有更好的方法。

在提交父母后,我使用 http://codebetter.com/karlseguin/2009/10/25/postgresql-day-2/ 中描述的 BulkCopy(它使用 NpgsqlCopyIn)来插入子记录。这太棒了。它在不到半秒的时间内插入数千条子记录。

我也很想将 BulkCopy 用于父记录。但我不知道如何从批量插入中取回主键值。

那么使用 C# 和 Npgsql 快速插入父子记录的诀窍是什么?答案可能就在某个地方,但显然我没有使用正确的搜索引擎参数。

非常感谢。

【问题讨论】:

    标签: c# postgresql npgsql


    【解决方案1】:

    当您使用serial 数据类型时,Postgres 会自动生成并分配一个序列。这很好,因为您可以出于其他目的劫持该序列,包括这个。

    这是我的建议。

    假设您的对象如下所示:

    public Parent
    {
        public long Id { get; set; }
        public string Description { get; set; }
        public List<Child> Children { get; set; }
    }
    
    public Child
    {
        public long Id { get; set; }
        public long ParentId { get; set; }
        public string Description { get; set; }
    }
    

    让您的代码根据顺序为每个父母分配一个 ID。这应该在眨眼之间发生:

    NpgsqlCommand cmd = new NpgsqlCommand("select nextval('schema.foo_id_seq')", conn);
    foreach (Parent p in parentList.Where(x => x.Id == null && x.Id == 0))
    {
        p.Id = Convert.ToInt64(cmd.ExecuteScalar());
        p.Children.ForEach(x => x.ParentId = p.Id);
    }
    

    如果这些记录尚不存在,Where 子句可能并不重要......只是需要考虑的事情。

    从这里开始,您的NpgsqlCopyIn 应该会为父母和孩子所用。

    【讨论】:

    • 感谢@Hambone,感谢所有提供意见的人。该解决方案完美运行。我对 ExecuteScalar 的调用速度印象深刻,只是在执行“select nextval ...”时。干杯!
    【解决方案2】:

    我会在一个文本文件中将父级插入脚本写入磁盘,然后通过常规命令运行它,以在一次往返数据库的过程中取回所有父级主键。

    【讨论】:

      【解决方案3】:

      这种情况的答案通常类似于"hi-lo" key generation。简而言之,这意味着您可以预先分配大量 ID 并在插入时指定它们,而不是让数据库在每次插入时生成 ID(强制您检索这些 ID)。这意味着您要自己设置每个父级的 ID,而不是将其留空(让 PostgreSQL 来做)。

      具体来说,您可以从管理父表 ID 的序列中检索一批 ID - 请参阅 this questionthis article 了解更多信息。然后,一旦您的应用程序中有 ID,您就可以批量插入具有这些 ID 的父级。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-06
        • 1970-01-01
        • 2012-11-06
        • 1970-01-01
        • 1970-01-01
        • 2013-05-18
        • 2016-11-07
        • 1970-01-01
        相关资源
        最近更新 更多