UPSERT з ON CONFLICT, використовуючи значення з вихідної таблиці в частині UPDATE


18

Подано:

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

Відповіді:


25

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

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

простий один вкладиш для манекенів типу: "виключений" вказує на нові вхідні дані, які ви хочете вставити або зберігати в таблиці.
користувач92674

8

Здійснюючи перейми в 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;

Для отримання додаткової інформації зверніться до офіційної документації або до цього простого вступу до версії .


Цей запит не працює.
shx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.