Вставить несколько строк, где не существует 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 6

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

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

Обновление: Для тех, кто приходит к этому с ситуацией, подобной моей, если у вас есть столбцы, которые нужно привести к типу (например, метки времени или 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 спас меня сегодня на проекте, но пришлось сделайте эти настройки, чтобы сделать его идеальным.