


INSERT ... ON CONFLICT позволяет задать действие при конфликте уникальности: пропустить вставку или обновить существующую строку. Для обновления нужно определить ключ конфликта, поддержанный подходящим уникальным индексом или ограничением.
На Mentoring.digital разберём учебную таблицу остатков по артикулам. Повторный импорт здесь передаёт новое абсолютное количество. Это важно: замена остатка и прибавление поступления являются разными операциями.
Выполняйте пример в отдельной учебной базе. Временная таблица существует в текущем сеансе и не требует изменения рабочих таблиц.
CREATE TEMP TABLE inventory_demo (
sku text PRIMARY KEY,
quantity integer NOT NULL CHECK (quantity >= 0)
);
INSERT INTO inventory_demo (sku, quantity)
VALUES ('BOOK', 5)
ON CONFLICT (sku) DO UPDATE
SET quantity = EXCLUDED.quantity
RETURNING sku, quantity;
INSERT INTO inventory_demo (sku, quantity)
VALUES ('BOOK', 8)
ON CONFLICT (sku) DO UPDATE
SET quantity = EXCLUDED.quantity
RETURNING sku, quantity;
Первый запрос вставляет BOOK с количеством 5. Второй встречает тот же sku и обновляет количество до 8. EXCLUDED обозначает предлагаемую для вставки строку. Это не имя обычной пользовательской таблицы.
ON CONFLICT (sku) DO NOTHING оставит существующую строку без изменения. Такой вариант подходит, если повторную запись действительно требуется игнорировать. Он не подтверждает, что данные входа совпали с уже сохранёнными.
Если вход означает новое поступление, правило обновления может быть другим:
INSERT INTO inventory_demo AS current_stock (sku, quantity)
VALUES ('BOOK', 2)
ON CONFLICT (sku) DO UPDATE
SET quantity = current_stock.quantity + EXCLUDED.quantity
RETURNING sku, quantity;
После предыдущих двух запросов получится 10. Но повторная доставка того же события прибавит 2 ещё раз. UPSERT сам по себе не делает обработку события идемпотентной. Для этого нужно отдельно определить уникальность события и порядок его учёта.
Между отдельной проверкой отсутствия строки и вставкой другой сеанс может вставить тот же ключ. ON CONFLICT связывает выбор вставки или обновления с проверкой уникальности внутри команды. Для DO UPDATE PostgreSQL гарантирует атомарный исход вставки или обновления, если не возникла другая ошибка.
Это не отменяет остальных ограничений таблицы, прав доступа, блокировок и возможных ошибок транзакции. Например, отрицательное количество нарушит CHECK. ON CONFLICT не предназначен для подавления любой ошибки записи.
В одной команде DO UPDATE нельзя дважды изменить одну и ту же существующую строку. Если пакет содержит повторяющийся sku, заранее определите правило: объединить количества, выбрать конкретную версию или отклонить пакет. Случайная потеря строки ради устранения ошибки меняет смысл импорта.
RETURNING показывает реально вставленные или обновлённые строки. При DO NOTHING пропущенная строка в этот результат не попадёт. Если добавлено условие WHERE для обновления и оно не выполнено, строка также не возвращается как обновлённая.
Проверьте новый ключ, существующий ключ, отрицательное количество и два одинаковых ключа внутри одного пакета. Основы запросов можно изучить на курсах SQL, а правила импорта и конкурентной записи разобрать с наставником по SQL.