Ответ 1
Это повторяющаяся проблема SELECT or INSERT
, связанная с (но отличная от) UPSERT. Новая функциональность UPSERT в Postgres 9.5 по-прежнему играет важную роль.
WITH ins AS (
INSERT INTO names(name)
VALUES ('bob')
ON CONFLICT ON CONSTRAINT names_name_key DO UPDATE
SET name = NULL
WHERE FALSE -- never executed, but locks the row
RETURNING id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM names
WHERE name = 'bob' -- only executed if no INSERT
LIMIT 1;
Таким образом, вы не написали новую версию строки без необходимости.
Предполагаю, что вы знаете, что в Postgres каждый UPDATE
записывает новую версию строки из-за своей модели MVCC - даже если name
установлено на то же значение, что и раньше. Это сделало бы операцию более дорогостоящей, добавив к возможным concurrency проблемам/блокировке в определенных ситуациях и раздув таблицу дополнительно.
Подробное объяснение и как обернуть это в функцию:
- Является ли SELECT или INSERT функцией, склонной к условиям гонки?
- Как использовать RETURNING с ON CONFLICT в PostgreSQL?
- Вернуть строки из INSERT с ON CONFLICT без необходимости обновления
Почему "исключенные" строки не включены в предложение RETURNING
?
Если параллельные UPDATE
или DELETE
(из другого сеанса) невозможны, вам не нужно блокировать строку и упростить:
WITH ins AS (
INSERT INTO names(name)
VALUES ('bob')
ON CONFLICT ON CONSTRAINT names_name_key DO NOTHING -- no lock needed
RETURNING id
)
SELECT id FROM ins
UNION ALL
SELECT id FROM names
WHERE name = 'bob' -- only executed if no INSERT
LIMIT 1;