我使用的是 C#.NET 4.0 (Visual Studio 2010)、PostgreSQL 9.2 和 Npgsql 2.0.12。我无法升级到 Npgsql 3。
我需要对父表进行快速插入,然后使用该插入中的主键对子表进行快速插入。
父表有一个定义为“serial”的列,它是主键。
子表有一个整数列,它是返回父表的外键。
并非每个父记录都会有子记录。 parent 可以有 0 个、1 个或多个 child 。
目前我正在将父对象缓冲到一个列表中。当缓冲了 5000 个 parent 时,从线程池中产生一个新线程将记录写入数据库。 (创建了一个新列表 主线程缓冲下一组父对象。) 新线程调用 NpgsqlConnection.BeginTransaction(),然后在循环内调用带参数的 NpgsqlCommand.ExecuteScalar() 以插入父记录并取回主键。 然后构建父对象的子对象(如果有)并保存到另一个列表。在循环结束时提交 parent 的事务。但是这种方法非常缓慢。插入 5000 条记录需要 3 到 10 秒的时间。当然有更好的方法。
parent 提交后,我使用 http://codebetter.com/karlseguin/2009/10/25/postgresql-day-2/ 中描述的 BulkCopy (使用 NpgsqlCopyIn)插入子记录。这太棒了。它在不到半秒的时间内插入数千条子记录。
我也喜欢将 BulkCopy 用于父记录。但我不知道如何从批量插入中取回主键值。
那么使用 C# 和 Npgsql 快速插入父记录和子记录的技巧是什么?答案可能就在某处,但显然我没有使用正确的搜索引擎参数。
非常感谢。
最佳答案
当您使用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
应该对 parent 和 child 来说都很棒。
关于c# - 使用 C# 和 Npgsql 快速插入父表和子表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41946686/