Вставить несколько строк, где не существует PostgresQL
Я хотел бы сгенерировать один sql-запрос для массовой вставки ряда строк, которые не существуют в таблице. Моя текущая установка делает новый запрос для каждой вставки записи, подобный решению, описанному в , где не существует в PostgreSQL, дает синтаксическую ошибку , но я хотел бы переместить это в один запрос для оптимизации производительности, так как моя текущая установка может генерировать несколько сотен запросов одновременно. Прямо сейчас я пытаюсь что-то вроде примера, который я добавил ниже:
INSERT INTO users (first_name, last_name, uid)
SELECT ( 'John', 'Doe', '3sldkjfksjd'), ( 'Jane', 'Doe', 'adslkejkdsjfds')
WHERE NOT EXISTS (
SELECT * FROM users WHERE uid IN ('3sldkjfksjd', 'adslkejkdsjfds')
)
Postgres возвращает следующую ошибку:
PG::Error: ERROR: INSERT has more target columns than expressions
Проблема в том, что PostgresQL, похоже, не хочет принимать ряд значений при использовании SELECT. И наоборот, я могу делать вставки, используя значения, но я не могу предотвратить создание дубликатов, используя WHERE NOT EXISTS.
Http://www.techonthenet.com/postgresql/insert.php предлагает в разделе пример-использование SUB-SELECT, что несколько записей должны вставляться из другая ссылочная таблица использует SELECT, поэтому мне интересно, почему я не могу передать ряд значений для вставки. Значения, которые я передаю, поступают из внешнего API, поэтому мне нужно сгенерировать значения для вставки вручную.
2 ответа:
Ваш
select
делает не то, что вы думаете.Самая компактная версия в PostgreSQL была бы примерно такой:
with data(first_name, last_name, uid) as ( values ( 'John', 'Doe', '3sldkjfksjd'), ( 'Jane', 'Doe', 'adslkejkdsjfds') ) insert into users (first_name, last_name, uid) select d.first_name, d.last_name, d.uid from data d where not exists (select 1 from users u2 where u2.uid = d.uid);
Что в значительной степени эквивалентно:
insert into users (first_name, last_name, uid) select d.first_name, d.last_name, d.uid from ( select 'John' as first_name, 'Doe' as last_name, '3sldkjfksjd' as uid union all select 'Jane', 'Doe', 'adslkejkdsjfds' ) as d where not exists (select 1 from users u2 where u2.uid = d.uid);
Обновление: Для тех, кто приходит к этому с ситуацией, подобной моей, если у вас есть столбцы, которые нужно привести к типу (например, метки времени или UUID или jsonb в PG 9.5), вы должны объявить, что в значениях, которые вы передаете запросу:
-- insert multiple if not exists -- where another_column_name is of type uuid, with strings cast as uuids -- where created_at and updated_at is of type timestamp, with strings cast as timestamps WITH data (id, some_column_name, another_column_name, created_at, updated_at) AS ( VALUES (<id value>, <some_column_name_value>, 'a5fa7660-8273-4ffd-b832-d94f081a4661'::uuid, '2016-06-13T12:15:27.552-07:00'::timestamp, '2016-06-13T12:15:27.879-07:00'::timestamp), (<id value>, <some_column_name_value>, 'b9b17117-1e90-45c5-8f62-d03412d407dd'::uuid, '2016-06-13T12:08:17.683-07:00'::timestamp, '2016-06-13T12:08:17.801-07:00'::timestamp) ) INSERT INTO table_name (id, some_column_name, another_column_name, created_at, updated_at) SELECT d.id, d.survey_id, d.arrival_uuid, d.gf_created_at, d.gf_updated_at FROM data d WHERE NOT EXISTS (SELECT 1 FROM table_name t WHERE t.id = d.id);
Ответ A_horse_with_no_name спас меня сегодня на проекте, но пришлось сделайте эти настройки, чтобы сделать его идеальным.