Представьте, что накопительный итог в отчёте изменяется сразу у нескольких строк с одинаковой датой. Какой механизм оконного кадра это объясняет?
Это объясняется тем, что оконный кадр типа RANGE обрабатывает строки с одинаковым значением сортировки как группу равных строк, или peer group. Поэтому все строки одной даты могут получить один и тот же накопительный результат. Если нужен построчный итог в физическом порядке строк, применяют ROWS и задают детерминирующую сортировку.
GROUP BY решает задачу свёртки данных: несколько исходных строк превращаются в одну строку на группу. Для аналитических отчётов этого часто недостаточно, поскольку необходимо сохранить каждую строку и одновременно вычислить сумму, ранг или накопительный показатель относительно набора строк.
Оконные функции появились как способ выполнять вычисления по связанному набору строк без схлопывания результата. Понятие оконного кадра уточняет, какие именно строки внутри окна участвуют в вычислении для текущей строки.
Предположим, строки отсортированы только по дате операции. У нескольких операций может быть одна и та же дата, поэтому порядок между ними не определён. При использовании RANGE текущая строка рассматривается вместе со всеми строками, имеющими такое же значение сортировки.
Из-за этого накопительный итог может не увеличиваться построчно: несколько строк с одной датой получат одинаковую сумму, включающую все операции этой даты. Если отчёт ожидает последовательное состояние после каждой операции, результат будет выглядеть неожиданно или окажется логически неверным.
ROWS задаёт кадр в терминах физических позиций строк. При сортировке по дате и уникальному идентификатору каждая следующая строка может добавлять свой вклад к накопительному итогу.
RANGE задаёт кадр по значениям выражения сортировки. Все строки, равные текущей строке по этому выражению, считаются соседями одного значения. Поэтому при сортировке только по дате строки одной даты обычно получают одинаковую границу кадра.
Минимальный пример:
В rows_total строки одной даты обрабатываются последовательно, хотя без уникального дополнительного ключа порядок между ними может быть нестабильным. В range_total все операции текущей даты входят в кадр каждой строки этой даты, поэтому значение обычно одинаково для всей группы.
Поведение кадра по умолчанию зависит от конкретной СУБД и формы оконного определения, поэтому полагаться на неявный кадр рискованно. Для отчётов следует явно указывать ROWS или RANGE, а при построчном порядке добавлять уникальный ключ сортировки.
Если бизнес-смысл показателя — итог по состоянию на дату, RANGE может быть правильным выбором. Если смысл — состояние после каждой отдельной операции, нужны ROWS и детерминированный порядок; при одинаковых временных метках обычно добавляют идентификатор операции или другой уникальный признак.
В платёжном отчёте требовалось показать баланс после каждой операции клиента. Несколько платежей часто имели одинаковое время до секунды. Первоначальный запрос сортировал операции только по времени и использовал накопительную сумму без явного кадра, поэтому операции с одинаковым временем получали одинаковый баланс.
Рассматривались три варианта:
Выбрали третий вариант: бизнес подтвердил, что идентификатор платёжной записи задаёт порядок при совпадении времени. После явного указания ROWS и уникального tie-breaker каждая строка стала отражать баланс после конкретной операции, а результаты перестали зависеть от плана выполнения.
1. Достаточно ли заменить RANGE на ROWS, не меняя сортировку?
Нет. ROWS разделяет строки по физическим позициям, но если сортировка не задаёт полный порядок, СУБД может выбрать порядок равных строк по-разному. Для воспроизводимого результата нужен уникальный дополнительный ключ сортировки.
2. Всегда ли одинаковые значения накопительного итога означают ошибку?
Нет. При показателе «итог на конец дня» одинаковая сумма для всех строк одной даты может быть именно требуемой семантикой. Ошибка возникает только тогда, когда от результата ожидают состояние после каждой отдельной строки.
3. Можно ли считать RANGE и ROWS взаимозаменяемыми, если сортировочное поле уникально?
При уникальном значении сортировки для каждой строки различие часто не проявляется: группы равных значений отсутствуют. Однако семантика всё равно различна, а при появлении дубликатов результат изменится, поэтому выбор кадра следует фиксировать явно в соответствии с бизнес-смыслом.