PostgreSQL - UPSERT с несколькими конфликтами - PullRequest
0 голосов
/ 17 октября 2019

После нескольких секунд игры с SQL я не могу достичь того, чего ожидаю. Я искал в документации PostgreSQL, но ничего не смог сделать так, как я хотел.

У меня есть таблица с 5 столбцами:

| id | some_serial | user_id |  foo   |  bar   |
|----|-------------|---------|--------|--------|
|  1 |     8000000 |    42   |   AA   | <null> |
|  2 |     8000001 |  <null> | <null> |   CC   |

Я хочу вставить новые значения в таблице, возвращающие соответствующий серийный номер. Если новые значения соответствуют каким-либо данным в user_id , foo или bar , я хочу, чтобы строка была обновлена, и здесь снова return соответствующий серийный номер.

Требуемое поведение:

Например: вставка 42, AA, BB обновит первую строку. bar станет BB, а 8000000 будет возвращено.

| id | some_serial | user_id |  foo   |  bar   |
|----|-------------|---------|--------|--------|
|  1 |     8000000 |    42   |   AA   |   BB   |
|  2 |     8000001 |  <null> | <null> |   CC   |

Вставка 43, null, CC обновит вторую строку. user_id станет 43, а 8000001 будет возвращено.

| id | some_serial | user_id |  foo   |  bar   |
|----|-------------|---------|--------|--------|
|  1 |     8000000 |    42   |   AA   |   BB   |
|  2 |     8000001 |    43   | <null> |   CC   |

Вставка 44, DD, EE создаст новую строку, возвращающую новый серийный номер.

| id | some_serial | user_id |  foo   |  bar   |
|----|-------------|---------|--------|--------|
|  1 |     8000000 |    42   |   AA   |   BB   |
|  2 |     8000001 |    43   | <null> |   CC   |
|  3 |     8000002 |    44   |   DD   |   EE   |

Фактическое поведение:

Я пытался с уникальным ограничением на (user_id, foo, bar), но это не дает мне желаемого поведения. После первой вставки результат будет:

| id | some_serial | user_id |  foo   |  bar   |
|----|-------------|---------|--------|--------|
|  1 |     8000000 |    42   |   AA   | <null> |
|  2 |     8000001 |  <null> | <null> |   CC   |
|  3 |     8000002 |    42   |   AA   |   BB   |

Насколько я понимаю, это связано с тем, что ограничение будет проверять уникальность трех столбцов вместе.

ОГРАНИЧЕНИЕ Iесть:

 ALTER TABLE my_table
   ADD CONSTRAINT my_table_user_id_foo_bar_unique
UNIQUE (user_id, foo, bar);

UPSERT У меня есть:

INSERT INTO my_table (user_id, foo, bar)
VALUES (42, 'AA', 'BB')
    ON CONFLICT ON CONSTRAINT my_table_user_id_foo_bar_unique
    DO UPDATE
   SET user_id = 42,
       foo = COALESCE(my_table.foo, EXCLUDED.foo),
       bar = COALESCE(my_table.bar, EXCLUDED.bar)
RETURNING some_serial;

Любая помощь будет принята с благодарностью. : -)

Здесь мое решение в это время:

Сначала я пытаюсь обновить строку, возвращая ее, если она существует.

UPDATE my_table
   SET user_id = $1
     , foo = COALESCE(foo, $2)
     , bar = COALESCE(bar, $3)
 WHERE (foo = $2 AND foo <> NULL)
    OR (bar = $3 AND bar <> NULL)
RETURNING some_serial

И если этот запрос ничего не дал, я просто вставляю его:

INSERT INTO my_table (user_id, foo, bar)
VALUES ( $1, $2, $3)
RETURNING some_serial
...