Як отримати ідентифікатор суперечливого ряду в пустелі?


18

У мене є таблиця tagз 2 стовпцями: id(uuid) та name(text). Зараз я хочу вставити новий тег у таблицю, але якщо тег уже існує, я хочу просто отримати idіснуючий запис.

Я припускав, що я можу просто використовувати ON CONFLICT DO NOTHINGв поєднанні з RETURNING "id":

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";

Але це повертає порожній набір результатів, якщо тег із назвою "foo" вже існує.

Потім я змінив запит, щоб використовувати DO UPDATEпункт noop :

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";

Це працює за призначенням, але це дещо заплутано, тому що я просто встановлюю ім’я на вже існуюче значення.

Це спосіб вирішити цю проблему чи є простіший підхід, якого я відсутній?


ти спробував returning excluded.id?
a_horse_with_no_name

@a_horse_with_no_name Це просто дає мені ERROR: missing FROM-clause entry for table "excluded"під час використання DO NOTHING.
Der Hochstapler

1
Дивіться також stackoverflow.com/a/42217872/1411457
harmic

Відповіді:


8

Це буде працювати (наскільки я тестував) у всіх 3 випадках, якщо значення, які потрібно вставити, усі нові або всі вже в таблиці або суміші:

WITH
  val (name) AS
    ( VALUES                          -- rows to be inserted
        ('foo'),
        ('bar'),
        ('zzz')
    ),
  ins AS
    ( INSERT INTO
        tag (name)
      SELECT name FROM val
      ON CONFLICT (name) DO NOTHING
      RETURNING id, name              -- only the inserted ones
    )
SELECT COALESCE(ins.id, tag.id) AS id, 
       val.name
FROM val
  LEFT JOIN ins ON ins.name = val.name
  LEFT JOIN tag ON tag.name = val.name ;

Можливо, є деякі інші способи зробити це, можливо, без використання нового ON CONFLICTсинтаксису.


4

Не маю ідеї, як це буде виконуватись, але як інший варіант спробувати, тут робимо той самий спосіб (без ON CONFLICT):

WITH items (name) AS (VALUES ('foo'), ('bar'), ('zzz')),
     added        AS
      (
        INSERT INTO tag (name)

        SELECT name FROM items
        EXCEPT
        SELECT name FROM tag

        RETURNING id
      )
SELECT id FROM added

UNION ALL

SELECT id FROM tag
WHERE name IN (SELECT name FROM items)
;

Тобто вставити лише [унікальні] імена, які не знайдені в tagтаблиці, і повернути ідентифікатори; комбінуйте це з ідентифікаторами імен, які існують у tag, для остаточного виводу. Ви також можете кинути nameу висновок, як це запропонував ypercubeᵀᴹ , щоб ви знали, який ідентифікатор відповідає якому імені.


1
Так. Останній ВИБІР мого коду також може бути записаний якSELECT .. FROM ins UNION ALL SELECT ... FROM val JOIN tag ... ;
ypercubeᵀᴹ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.