В практическом сервисе после UPDATE нужно сразу получить значения изменённых строк без повторного SELECT. К...

В практическом сервисе после UPDATE нужно сразу получить значения изменённых строк без повторного SELECT. Какой механизм PostgreSQL решает эту задачу?

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

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

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

UPDATE accounts SET status = 'blocked' WHERE risk_score >= 90 RETURNING account_id, status;

Запрос вернёт идентификаторы и новые значения статуса всех строк, затронутых UPDATE.

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

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

Предложение RETURNING решает эту задачу в рамках одного оператора. В PostgreSQL это расширение SQL, предназначенное для получения результата DML без дополнительного сетевого обмена и отдельного окна между изменением и чтением.

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

Отдельный SELECT после UPDATE создаёт лишний запрос и усложняет согласованность данных. Между двумя операторами другая транзакция или триггер может изменить строку, поэтому приложение способно прочитать уже не тот результат, который был получен непосредственно при обновлении.

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

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

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

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

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

Механизм не является полностью переносимым: синтаксис и возможности RETURNING зависят от СУБД. В PostgreSQL вызывающий пользователь также должен иметь необходимые права на чтение столбцов, указанных в RETURNING.

Значения могут отражать действие BEFORE-триггеров, поскольку PostgreSQL возвращает итоговые значения строки с учётом соответствующей модификации. Поэтому RETURNING полезнее, чем локальное повторение логики вычисления новых полей.

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

Сервис блокирует высокорисковые счета и должен отправить событие для каждого реально обработанного счёта. Возможны два подхода: выполнить UPDATE, затем SELECT по исходному условию, либо использовать UPDATE с RETURNING.

Первый вариант проще на уровне отдельных SQL-операторов, но требует дополнительного запроса и может прочитать изменённые конкурентной транзакцией данные. Кроме того, исходное условие может уже не описывать тот же набор строк.

Выбран UPDATE с RETURNING: база данных атомарно определяет затронутые строки и сразу возвращает нужные идентификаторы. Сервис публикует события по этому результату, сокращает число обращений к базе и не дублирует вычислительную логику приложения.

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

  1. Возвращает ли RETURNING строки, если UPDATE не изменил фактическое значение столбца?

Да. Если строка удовлетворила WHERE и была обработана оператором UPDATE, она обычно попадает в RETURNING, даже когда новое значение равно старому. RETURNING связан с обработанными строками, а не с проверкой фактического отличия каждого значения.

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

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

  1. Гарантирует ли RETURNING, что приложение получит результат даже при ошибке оператора?

Нет. Если UPDATE завершается ошибкой, например из-за нарушения ограничения, успешного результата RETURNING для всей операции нет; в транзакционном сценарии изменения обычно откатываются. Поэтому обработчик должен учитывать ошибки DML и не публиковать события как подтверждённые только на основании попытки выполнения запроса.