UPSERT с ON CONFLICT, используя значения из исходной таблицы в части UPDATE

17

Данный:

CREATE TABLE A (
PK_A INT8 NOT NULL,
A INT8,
PRIMARY KEY (PK_A)
);

CREATE TABLE B (
PK_B INT8 NOT NULL,
B INT8,
PRIMARY KEY (PK_B)
);

Этот запрос:

insert into table_b (pk_b, b) 
select pk_a,a from table_a 
on conflict (b) do update set b=a;

вызывает следующую ошибку:

ERROR:  column "a" does not exist
LINE 1: ...elect pk_a,a from table_a on conflict (b) do update set b=a;
                                                                 ^
HINT:  There is a column named "a" in table "*SELECT*", but it cannot be referenced from this part of the query.

Как сделать обновление при обращении к содержанию table_a?

Тони Индрали
источник
5
CREATE TABLE A...создает таблицу a, а не table_a.
Абелисто
do update set b = a;не может найти «а» , потому что там ссылку на таблицу «Ъ» и не Подзапрос, попробуйтеdo update set b = (select a from a);
Patrick7

Ответы:

24

Множество проблем.
Ваша настройка, расширенная:

CREATE TABLE a (
  pk_a int PRIMARY KEY 
, a int
, comment text  -- added column to make effect clear
);

CREATE TABLE b (
  pk_b int PRIMARY KEY
, b int 
, comment text
);

INSERT INTO a VALUES (1, 11, 'comment from a')
                   , (2, 22, 'comment from a');

INSERT INTO b VALUES (1, 77, 'comment from b');

Это работает:

INSERT INTO b (pk_b, b, comment) 
SELECT pk_a, a, comment
FROM   a 
ON     CONFLICT (pk_b) DO UPDATE  -- conflict is on the unique column
SET    b = excluded.b;            -- key word "excluded", refer to target column

Результат:

TABLE b;

 pk_b | b  |    comment
------+----+----------------
    1 | 11 | comment from b   -- updated
    2 | 22 | comment from a   -- inserted

Проблемы

  1. Вы сбиваете с толку table_aи Aв своей демонстрации (как прокомментировал @Abelisto ).

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

  2. Как упоминалось @Ziggy , ON CONFLICTработает только для фактических нарушений уникальных или исключающих ограничений . Руководство:

    Необязательное ON CONFLICTпредложение определяет альтернативное действие по отношению к выдаче уникальной ошибки нарушения или нарушения ограничения исключения.

    Следовательно, ON CONFLICT (b)не может работать, никаких ограничений нет. ON CONFLICT (pk_b)работает.

  3. Как и в случае @Ziggy , имена исходных таблиц не видны в этой UPDATEчасти. Руководство:

    SETИ WHEREположение в ON CONFLICT DO UPDATEимеет доступ к существующей строке с использованием имени таблицы (или псевдонима) и строки , предложенных для вставки с помощью специальной excludedтаблицы .

    Жирный акцент мой.

  4. Вы также не можете использовать имена столбцов исходной таблицы в UPDATEдетали. Это должны быть имена столбцов целевой строки . Итак, вы действительно хотите:

    SET    b = excluded.b

    Руководство еще раз:

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

Эрвин Брандштеттер
источник
спасибо за это объяснение, теперь я знаю, почему b = excluded.aне может работать, это было немного спрятано в официальной документации.
Patrick7
7

При выполнении upserts в PostgreSQL 9.5+ вы должны ссылаться на исключенные данные (те, которые не удалось вставить) по псевдониму excluded. Также on conflictопция должна ссылаться на ключ: (pk_b)вместо (b). Например.

insert into table_b (pk_b, b) 
select pk_a,a from table_a 
on conflict (pk_b) do update set b=excluded.b;

Для получения дополнительной информации обратитесь к официальной документации или этому простому введению upsert .

Ziggy Crueltyfree Zeitgeister
источник
Этот запрос не работает.
ШХ