Если бы у нас были только агрегатные функции, мы были бы принуждены постоянно делать выбор: видеть детальные данные или агрегированные? Аналитические функции, известные также как оконные функции или over-функции, позволяют одновременно получить и то и другое. С их помощью можно видеть данные, агрегированные по множеству строк, рядом с детальными данными.
Обычные, или скалярные, функции принимают на вход значения столбцов одной — текущей — строки курсора. Агрегатные функции принимают на вход значения столбцов всех строк курсора, определяемых where
и group by
(или их отсутствием). Оконные функции принимают на вход значения столбцов множества строк, определяемого в предложении over
и зависящего от текущей строки.
Рассмотрим оконные функции на примере СУБД PosgreSQL 15.
Для удобства обзора, разделим все оконные функции на три группы:
- агрегатные функции,
- ранжирующие функции,
- относительные функции.
Обычные агрегатные функции, кроме функций на упорядоченных множествах (с предложением within group
), можно использовать как оконные функции, добавив к их вызову предложение over
.
Ранжирующие и относительные функции нужно обязательно вызывать с предложением over
.
Агрегатные функции
Ниже список агрегатных функций PosrgreSQL 15, исключая статистические функции и функции на упорядоченных множествах. Подробнее см. Aggregate Functions.
Функция | Описание |
---|---|
array_agg | Массив значений. |
avg | Среднее арифметическое. |
bit_and | Побитовое И. |
bit_or | Побитовое ИЛИ. |
bit_xor | Побитовое исключающее ИЛИ. |
bool_and | Логическое И. |
bool_or | Логическое ИЛИ. |
count | Счетчик значений. |
every | Стандартный эквивалент bool_and. |
json_agg | Массив json. |
jsonb_agg | Массив jsonb. |
json_object_agg | Объект json. |
jsonb_object_agg | Объект jsonb. |
max | Максимальное значение. |
min | Минимальное значение. |
range_agg | Объединение диапазонов. |
range_intersect_agg | Пересечение диапазонов. |
string_agg | Конкатенация строк. |
sum | Сумма значений. |
xmlagg | Агрегат xml. |
И пара примеров их использования в качестве оконных.
Агрегатная функция с order by
в предложении over
и оконной рамкой (window frame) по умолчанию производит накопительный эффект от строки к строке результирующей таблицы.
select n,
count(n) over (order by n) count_,
sum(n) over (order by n) sum_,
avg(n) over (order by n) avg_
from
generate_series(1,5) as t(n)
;
n | count_ | sum_ | avg_ |
---|---|---|---|
1 | 1 | 1 | 1.0 |
2 | 2 | 3 | 1.5 |
3 | 3 | 6 | 2.0 |
4 | 4 | 10 | 2.5 |
5 | 5 | 15 | 3.0 |
select n,
string_agg(n::text, ', ') over (order by n) asc_,
string_agg(n::text, ', ') over (order by n desc) desc_
from
generate_series(1,5) as t(n)
;
n | asc_ | desc_ |
---|---|---|
1 | 1 | 5, 4, 3, 2, 1 |
2 | 1, 2 | 5, 4, 3, 2 |
3 | 1, 2, 3 | 5, 4, 3 |
4 | 1, 2, 3, 4 | 5, 4 |
5 | 1, 2, 3, 4, 5 | 5 |
Ранжирующие функции
Ниже список ранжирующих функций PosrgreSQL 15 и пара примеров их использования. Подробнее см. Window Functions.
Функция | Описание |
---|---|
row_number | Номер текущей строки. |
rank | Ранг текущей строки, с пропусками; это номер первой строки в группе равнозначных ей (its peer group). |
dense_rank | Ранг текущей строки, без пропусков; это номер группы равнозначных строк (peer groups). |
percent_rank | Ранг текущей строки в диапазоне от 0 до 1. |
cume_dist | Кумулятивное распределение в диапазоне от 1/(число строк в окне) to 1. |
ntile(arg) | Номер группы (bucket) от 1 до arg , в которую попадает текущая строка. |
Все перечисленные функции нуждаются в упорядочивании входных данных с помощью order by
в предложении over
. Строки, которые не отличаются друг от друга значениями столбцов в order by
, называются равнозначными (peers).
Функции rank
, dense_rank
, percent_rank
и cume_dist
возвращают одинаковые значения для всех строк из группы равнозначных.
select n,
dense_rank() over (order by n) dense_rank_,
percent_rank() over (order by n) percent_rank_,
cume_dist() over (order by n) cume_dist_
from
generate_series(1,5) as t(n)
;
n | dense_rank_ | percent_rank_ | cume_dist_ |
---|---|---|---|
1 | 1 | 0.0 | 0.2 |
2 | 2 | 0.25 | 0.4 |
3 | 3 | 0.5 | 0.6 |
4 | 4 | 0.75 | 0.8 |
5 | 5 | 1.0 | 1.0 |
select n,
ntile(2) over (order by n) ntile_2,
ntile(3) over (order by n) ntile_3
from
generate_series(1,10) as t(n)
;
n | ntile_2 | ntile_3 |
---|---|---|
1 | 1 | 1 |
2 | 1 | 1 |
3 | 1 | 1 |
4 | 1 | 1 |
5 | 1 | 2 |
6 | 2 | 2 |
7 | 2 | 2 |
8 | 2 | 3 |
9 | 2 | 3 |
10 | 2 | 3 |
Относительные функции
Ниже список относительных функций PosrgreSQL 15 и пара примеров их использования. Подробнее см. Window Functions.
Функция | Описание |
---|---|
lag | Значение из строки, предшествующей текущей. |
lead | Значение из строки, следующей за текущей. |
first_value | Значение из первой строки окна (window frame). |
last_value | Значение из последней строки окна (window frame). |
nth_value | Значение из n-ной строки окна (window frame). |
select n,
lag(n) over (order by n) lag_,
lead(n) over (order by n) lead_
from
generate_series(1,5) as t(n)
;
n | lag_ | lead_ |
---|---|---|
1 | 2 | |
2 | 1 | 3 |
3 | 2 | 4 |
4 | 3 | 5 |
5 | 4 |
Заметьте, что функции first_value
, last_value
и nth_value
имеют дело только со строками в оконной рамке (window frame), которая по умолчанию содержит строки с первой строки окна до последней из равнозначных текущей строке.
select n,
first_value(n) over (order by n) first_,
nth_value(n, 3) over (order by n) nth_,
last_value(n) over (order by n) last_
from
generate_series(1,5) as t(n)
;
n | first_ | nth_ | last_ |
---|---|---|---|
1 | 1 | 1 | |
2 | 1 | 2 | |
3 | 1 | 3 | 3 |
4 | 1 | 3 | 4 |
5 | 1 | 3 | 5 |
Оконные рамки
Оконная рамка (window frame) определяет часть строк окна, на которых работает оконная функция.
В следующем примере значения столбцов first_
и last_
вычисляются в оконной рамке по умолчанию, а значения столбцов first_x
и last_x
вычисляются в оконной рамке, включающей текущую строку и все строки до конца окна:
select n,
first_value(n) over (order by n) first_,
first_value(n) over (order by n rows between current row and unbounded following) first_x,
last_value(n) over (order by n) last_,
last_value(n) over (order by n rows between current row and unbounded following) last_x
from
generate_series(1,5) as t(n)
;
n | first_ | first_x | last_ | last_x |
---|---|---|---|---|
1 | 1 | 1 | 1 | 5 |
2 | 1 | 2 | 2 | 5 |
3 | 1 | 3 | 3 | 5 |
4 | 1 | 4 | 4 | 5 |
5 | 1 | 5 | 5 | 5 |
Сузив оконные рамки до одной предыдущей и одной последующей строки, сымитируем функции lag
и lead
с помощью функций first_value
и last_value
:
select n,
first_value(n) over (order by n rows between 1 preceding and 1 preceding) lag_,
last_value(n) over (order by n rows between 1 following and 1 following) lead_
from
generate_series(1,5) as t(n)
;
n | lag_ | lead_ |
---|---|---|
1 | 2 | |
2 | 1 | 3 |
3 | 2 | 4 |
4 | 3 | 5 |
5 | 4 |
Помимо rows
, оконная рамка может определяться через range
и groups
. Подробнее см. описание frame_clause
в документации PostgreSQL.
Больше окон, полезных и разных
В вышеприведенных примерах оконные функции работают в рамках одного окна, которое включает все строки, порождаемые частями from
и where
запроса select
.
Но предложение partition by
в over
позволяет разбить это множество строк на группы, чтобы оконные функции работали отдельно со строками каждой из них – аналогично тому, как group by
порождает группы строк для агрегатных функций. Таким образом, partition by разбивает множество строк на ряд окон.
Рассмотрим такой набор данных:
select
case n % 3
when 0 then
'A'
when 1 then
'B'
when 2 then
'C'
end item,
n
from generate_series(1, 9) as t(n)
;
item | n |
---|---|
B | 1 |
C | 2 |
A | 3 |
B | 4 |
C | 5 |
A | 6 |
B | 7 |
C | 8 |
A | 9 |
Тогда все множество строк можно разбить на группы по значению item
:
select
item,
n,
count(n) over (partition by item order by n) count_,
lag(n) over (partition by item order by n) lag_
from (
select
case n % 3
when 0 then
'A'
when 1 then
'B'
when 2 then
'C'
end item,
n
from generate_series(1, 9) as t(n)
) t
;
item | n | count_ | lag_ |
---|---|---|---|
A | 3 | 1 | |
A | 6 | 2 | 3 |
A | 9 | 3 | 6 |
B | 1 | 1 | |
B | 4 | 2 | 1 |
B | 7 | 3 | 4 |
C | 2 | 1 | |
C | 5 | 2 | 2 |
C | 8 | 3 | 5 |
Подробнее см. описание window_definition
в документации PostgreSQL.
Что дальше
Оконные функции, а частности, используются при построении запросов вида "пробелы и острова", или "gaps and islands". См. мой пост Пробелы и острова, или Gaps and islands. Часть I.
Ранжирующие функции, а частности, помогают строить запросы вида "верхние-n", или "top-n", где требуется получить наименьшие или наибольшие по некоторому критерию n строк.
SQL неисчерпаем!
Комментариев нет:
Отправить комментарий