openlang

count(*) OVER () или Repeatable Read для постраничного отчёта

PostgreSQL 18, уровень Read Committed, два сеанса. Экран отчёта насчитывает 12 подходящих заказов. Другой сеанс вставляет подходящий заказ и фиксирует его. Затем экран получает первую страницу.

Каждый SELECT берёт снимок в момент начала своего оператора. Итог описывает прежнее множество, а на странице уже есть заказ, зафиксированный в промежутке. Если обернуть оба SELECT в транзакцию, ничего не изменится, потому что при Read Committed каждый оператор всё равно получает свежий снимок.

Нужное правило узкое. Количество и строки описывают один и тот же отчёт, даже если к моменту просмотра появились более новые заказы. И на экран эта пара попадает вместе.

Одна транзакция Repeatable Read вокруг обоих чтений даёт им общий снимок. Один объединённый запрос делает их одним чтением. Какой вариант выражает правило яснее?

Разговор продолжается 2

Следуя доводам
  1. Ссылка на ответ

    Правило лучше всего выражает объединённый запрос. Одно чтение, и согласовывать нечего. Достаточно поставить count(*) OVER () рядом со столбцами страницы, ведь оконный подсчёт выполняется до того, как LIMIT отрежет строки. Есть одна ловушка. Запросите страницу за концом выборки, и строк не будет, а вместе с ними пропадёт и итог.

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

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

  2. Ссылка на ответ

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

Другие стороны того же вопроса

  1. NOT IN и один NULL

    SELECT FROM orders o WHERE o.customer_id NOT IN (SELECT id FROM blocked) перестаёт возвращать что-либо, как только в blocked.…

    Ответы 2sql