Программирование SQLDML и запросыРазработчик серверной части PostgreSQL

Как в PostgreSQL одновременно удалить строки и получить их прежние значения?

Как в PostgreSQL одновременно удалить строки и получить их прежние значения?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

В PostgreSQL для этого используется предложение RETURNING у оператора DELETE. Оно возвращает по одной результирующей строке для каждой фактически удалённой строки, поэтому отдельный предварительный SELECT не нужен.

Исторический контекст

Обычная последовательность «сначала выбрать строки, затем удалить их» создаёт промежуток между двумя операторами. За это время другой сеанс может изменить данные, а приложение может получить неполный результат или удалить не совсем те строки, которые были выбраны.

RETURNING решает задачу получения затронутых строк в рамках одного DML-оператора. Это удобно для журналирования, передачи идентификаторов удалённых объектов и синхронизации с прикладным кодом.

Постановка проблемы

После удаления приложению часто нужны идентификаторы, владельцы или другие значения удалённых строк. Предварительный SELECT усложняет код и может привести к рассогласованию между прочитанными и фактически удалёнными данными.

Нужно учитывать, что возвращаются только строки, действительно затронутые оператором. Если условие не совпало ни с одной строкой, результат будет пустым.

Подробное решение

RETURNING вычисляется для строк, обработанных DELETE, и позволяет указать как отдельные столбцы, так и выражения над их значениями.

DELETE FROM accounts WHERE account_id = 42 RETURNING account_id, owner_id, email;

Если строка с таким идентификатором существует и удаление разрешено, запрос вернёт её значения. Если строка отсутствует или удаление ограничено условием, соответствующей строки в результате не будет.

Это не отдельный SELECT и не предварительная проверка: удаление и получение результата выполняются одним оператором. Поэтому приложение может использовать возвращённые данные, например, для записи события аудита.

Поведение ограничений и триггеров зависит от их определения. В частности, внешний ключ может запретить удаление или вызвать каскадное удаление, а триггеры могут изменить итоговое поведение операции. RETURNING также не делает операцию автоматически независимой от транзакции: фиксация результата для других сеансов определяется COMMIT.

Предложение RETURNING не является одинаково доступным во всех СУБД. В PostgreSQL это штатный механизм; в других системах могут применяться аналоги, например OUTPUT в Microsoft SQL Server, либо отдельные запросы.

Ситуация из практики

Сервис удаляет пользовательскую сессию и должен сразу отправить в журнал её идентификатор и владельца. Вариант с предварительным SELECT требует двух операторов и аккуратной обработки конкурирующих изменений. Вариант с удалением, за которым следует SELECT, уже не сможет надёжно прочитать удалённую строку.

Использование DELETE с RETURNING выполняет обе задачи атомарно: строка удаляется, а её данные возвращаются приложению из результата того же оператора. Это уменьшает число обращений к базе данных и исключает рассогласование между выборкой и удалением. Если аудит должен быть гарантированным, его обычно выполняют в той же транзакции или передают данные в надёжный механизм журналирования.

Что кандидаты часто упускают

  1. Что вернёт DELETE с RETURNING, если условие не нашло строк?

Он вернёт пустой результирующий набор, а не NULL и не специальную строку. Приложение должно отдельно учитывать количество возвращённых строк, особенно если ожидалось удаление ровно одной записи.

  1. Гарантирует ли RETURNING, что удалённые данные уже окончательно исчезли для всех сеансов?

Нет. Предложение возвращает результат выполненной операции, но видимость изменений для других транзакций определяется уровнем изоляции и фиксацией транзакции. До COMMIT удаление может быть невидимым другим сеансам, а при ROLLBACK оно будет отменено.

  1. Заменяет ли RETURNING аудит изменений?

Не полностью. Он передаёт удалённые значения вызывающему коду, но сам по себе не сохраняет их в журнале и не гарантирует доставку события во внешнюю систему. Для надёжного аудита нужны запись в таблицу в той же транзакции, триггер или другой специально выбранный механизм.