Я пытаюсь добиться абстракции базы данных в своем проекте, но теперь я застрял в том, что делаю большую часть INSERT в PostgreSQL. Мой проект находится в C #, и я использую PostgreSQL 9.3 с npgsql.dll 2.0.14.
Для Microsoft SQL Server я делаю массовый INSERT просто путем конкатенации всех операторов и выполнения ExecuteNonQuery:
IF NOT EXISTS (SELECT id FROM table WHERE id = 1) INSERT INTO table (id) VALUES (1);
IF NOT EXISTS (SELECT id FROM table WHERE id = 2) INSERT INTO table (id) VALUES (2);
IF NOT EXISTS (SELECT id FROM table WHERE id = 3) INSERT INTO table (id) VALUES (3);
Хотя предложение IF-NOT-EXISTS может быть заменено в PostgreSQL с помощью SELECT-WHERE, этот подход, к сожалению, пока не работает, потому что каждый отдельный оператор в PostgreSQL выполняется отдельно.
Таким образом, я googled для другого решения и нашел подход использования команды COPY
вместе с NpgsqlCopySerializer / NpgsqlCopyIn, чтобы выполнить «поток» объемных данных. Но теперь я все время сталкиваюсь с ошибками нарушения первичного ключа - потому что предложение EXISTS / WHERE, по-видимому, не может использоваться вместе с инструкцией COPY
.
Я бы очень хотел, чтобы я не делал INSERT все один за другим, так как это очень замедлит мое приложение, поэтому я надеюсь, что кто-нибудь еще решит эту проблему!
Как правило, для такого типа ситуаций у меня будет отдельная промежуточная таблица, в которой нет ограничения PK, которое я бы заполнил с помощью COPY
(предполагая, что данные были в формате, для которого имеет смысл делать COPY
). Тогда я бы сделал что-то вроде:
insert into table
select a.*
from staging a
where not exists (select 1
from table
where a.id = b.id)
Этот подход не слишком далек от вашего оригинального дизайна.
Однако я не полностью понимаю эту часть вашего вопроса, что даже не кажется абсолютно актуальным для вашего вопроса:
этот подход, к сожалению, пока не работает, потому что каждый отдельный оператор в postgreSQL выполняется отдельно.
Это совсем не так, а не для РСУБД. Конечно, автообключение может быть включено на вашем клиенте, но это не означает, что postgres фиксирует каждое заявление отдельно и что вы не можете отключить автоматическое совершение. Такой подход будет работать:
begin;
insert into table (id) select 1 where not exists (select 1 from table where id = 1);
insert into table (id) select 2 where not exists (select 1 from table where id = 2);
insert into table (id) select 3 where not exists (select 1 from table where id = 3);
commit;
Однако, как вы указали, если у вас есть больше, чем несколько таких заявлений, вы быстро столкнетесь с некоторыми проблемами производительности.