使用C#和Npgsql

时间:2017-01-30 22:40:08

标签: c# postgresql npgsql

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

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

父表的列定义为" serial"这是主键。

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

并非每条父记录都会有孩子。父母可以有0个,1个或多个孩子。

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

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

我也喜欢将BulkCopy用于父记录。但我无法弄清楚如何从批量插入中获取主键值。

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

非常感谢。

3 个答案:

答案 0 :(得分:0)

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

具体而言,您将从管理父表ID的序列中检索一批ID - 有关详细信息,请参阅this questionthis article。然后,一旦您的应用程序中有ID,您就可以批量插入具有这些ID的父项。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

当您使用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; }
}

让您的代码根据序列为每个Parent分配一个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应该为父母和孩子们提供帮助。