Два SELECT в одной транзакции дали разные итоги: снимок PostgreSQL

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

При стандартном для PostgreSQL уровне Read Committed каждый запрос получает новый снимок зафиксированных данных. Граница транзакции сама по себе не закрепляет один набор строк для всех SELECT. Между двумя командами другая транзакция может зафиксировать заказ.

Как изменить решение

BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY;
SELECT sum(amount) FROM orders WHERE status = 'paid';
SELECT id, amount FROM orders WHERE status = 'paid' ORDER BY id;
COMMIT;

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

Если можно получить итог и детали одним SQL-запросом, часто удобнее сделать это через оконную сумму или отдельный CTE. Тогда меньше сетевых обращений и проще понимать момент чтения. Repeatable Read полезен, когда отчёт действительно состоит из нескольких самостоятельных выборок.

Что проверить на своих данных

Запустите первое чтение в соединении A, вставьте и зафиксируйте новый оплаченный заказ в B, затем выполните детали в A. Сначала повторите при Read Committed, затем при Repeatable Read. Проверяйте именно наличие нового id, чтобы разница не потерялась среди округлений.

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

Практика дальше

Согласованное чтение нужно довести до понятного ответа API. В курсе FastAPI вы строите backend с SQLAlchemy и проверяете обработчики через TestClient и pytest. Проект помогает связать работу с данными, HTTP-статусы и тесты на SQLite.

Посмотреть программу и практику курса

Источник: Два SELECT в одной транзакции дали разные итоги — официальная документация.

Другие задачи по теме: PostgreSQL: запись, блокировки и планы запросов.

Продолжить на практике