2017-01-30 6 views
2

Я использую C# .NET 4.0 (Visual Studio 2010), PostgreSQL 9.2 и Npgsql 2.0.12. Я не могу перейти на Npgsql 3.Быстрая вставка в родительские и дочерние таблицы с использованием C# и Npgsql

Мне нужно сделать быструю вставку в родительскую таблицу, а затем, используя первичные ключи из этой вставки, сделать быструю вставку в дочернюю таблицу.

В родительской таблице есть столбец, определяемый как «серийный», который является первичным ключом.

В дочерней таблице есть целочисленный столбец, являющийся внешним ключом, к родительской таблице.

Не каждая родительская запись будет иметь детей. У родителя может быть 0, 1 или много детей.

В настоящее время я буферизую родительские объекты в список. Когда 5000 родителей были буферизованы, создайте новый поток из пула потоков, чтобы записать записи в базу данных. (Создается новый список для основного потока для буферизации следующего набора родительских объектов.) Новый поток вызывает NpgsqlConnection.BeginTransaction(), а внутри цикла вызывает NpgsqlCommand.ExecuteScalar() с параметрами для вставки родительской записи и вернуть первичный ключ. Затем создайте дочерний объект родителя, если он есть, и сохраните его в другом списке. В конце цикла зафиксируйте транзакцию родителей. Но эта методология BRUTALLY медленно. В любом месте от 3 до 10 секунд, чтобы вставить 5000 записей. Конечно, есть лучший способ.

После того, как родители были совершены, я использую BulkCopy, описанный в http://codebetter.com/karlseguin/2009/10/25/postgresql-day-2/ (который использует NpgsqlCopyIn) для вставки дочерних записей. Это работает FANTASTIC. Он вставляет несколько тысяч дочерних записей менее чем за полсекунды.

Я хотел бы использовать этот BulkCopy для родительских записей. Но я не могу понять, как вернуть значения первичного ключа из объемной вставки.

Итак, каков трюк для быстрой вставки родительских и дочерних записей с использованием C# и Npgsql? Ответ, вероятно, где-то есть, но, очевидно, я не использую правильные параметры поисковой системы.

Большое спасибо за внимание.

ответ

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; } 
} 

Есть код присвоить каждому Родитель идентификатор, основанный на последовательности. Это должно произойти в мгновение ока:

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 должен утешиться как для родителей, так и для детей.

+0

Спасибо @ Хамбоне, и всем, кто предложил вход. Это решение отлично работало. Я перестаю впечатлен тем, насколько быстро вызывается вызов ExecuteScalar, когда просто «выберите nextval ...». Ура! – TJH

0

Ответ на этот сценарий обычно имеет значение "hi-lo" key generation. В двух словах это означает, что вместо того, чтобы генерировать идентификаторы базы данных для каждой вставки (заставляя вас извлекать эти идентификаторы), вы можете предварительно выделить большое количество идентификаторов и указать их при вставке. Это означает, что вы сами устанавливаете идентификатор для каждого родителя, а не оставляете его пустым (и позволяете PostgreSQL делать это).

Конкретно, вы должны получить партию идентификаторов из последовательности, управляющей идентификатором родительской таблицы - см. this question и this article для получения дополнительной информации. Затем, как только у вас есть идентификаторы в приложении, вы должны вставлять родителям эти идентификаторы.

0

Я хотел бы написать родительский сценарий вставки в диск в текстовом файле, а затем запустить его через регулярную команду, чтобы вернуть все первичные ключи родителей в одну поездку туда и обратно в базу данных.