# Оконные функции

[Оконная функция](../../overview/glossary.md#window_function) отличается от
скалярной или агрегатной наличием собственного [окна](../../overview/glossary.md#window).
Параметры окна описываются в запросе с помощью выражения `OVER`. Также, для
каждой оконной функции может быть дополнительно задано фильтрующее выражение
`FILTER`. Таким образом, если функция применяется к окну с помощью `OVER`,
то она считается оконной. В противном случае — агрегатной или скалярной.

!!! note "Примечание"
    Оконные функции разрешены только в проекции оператора `SELECT`.

## Синтаксис {: #syntax }

![Window](../../images/ebnf/window.svg)

### Раздел {: #partition }

??? note "Диаграмма"
    ![Partition](../../images/ebnf/partition.svg)

### Выражение {: #expression }

??? note "Диаграмма"
    ![Expression](../../images/ebnf/expression.svg)

### Литерал {: #literal }

??? note "Диаграмма"
    ![Literal](../../images/ebnf/literal.svg)

## Параметры {: #params }

Примеры в этом разделе предполагают, что база данных содержит следующие
данные:

```sql
CREATE TABLE t1(a INTEGER PRIMARY KEY, b text, c text) DISTRIBUTED BY (a);

INSERT INTO t1 VALUES   (1, 'A', 'one'  ),
                        (2, 'B', 'two'  ),
                        (3, 'C', 'three'),
                        (4, 'D', 'one'  ),
                        (5, 'E', 'two'  ),
                        (6, 'F', 'three'),
                        (7, 'G', 'one'  );
```

### Фильтрация {: #filter }

Если указано выражение `FILTER`, в набор данных для оконной функции включаются
только те кортежи, для которых значение фильтрующего выражения истинно.

!!! note "Примечание"
    `FILTER` может использоваться только для агрегатных оконных функций.

#### Пример фильтрации {: #example_filter }

```sql
SELECT c, a, b, group_concat(b, '.') FILTER (WHERE c!='two') OVER (
  ORDER BY a
)
FROM t1 ORDER BY a;

   c   | a | b |    col_1
-------+---+---+-------------
 one   | 1 | A | "A"
 two   | 2 | B | "A"
 three | 3 | C | "A.C"
 one   | 4 | D | "A.C.D"
 two   | 5 | E | "A.C.D"
 three | 6 | F | "A.C.D.F"
 one   | 7 | G | "A.C.D.F.G"
```

В этом запросе `FILTER (WHERE c!='two')` исключает кортежи, в которых `c = 'two'`,
из рамок окна. Однако в итоговом наборе данных кортежи с `c = 'two'` остаются,
и им присваивается результат, вычисленный только по отфильтрованным кортежам.
Например, в кортеже `(2, 'B')` значение `group_concat` остается `A`, так как
`B` не входит в рамки окна.

### Разделение и упорядочивание {: #partition_ordering }

При вычислении оконных функций результирующий набор запроса делится на один
или несколько [разделов](../../overview/glossary.md#partition). Раздел включает
все кортежи, имеющие одинаковые значения для всех выражений в `PARTITION BY`.
Если `PARTITION BY` отсутствует, весь результат запроса считается одним разделом.
Обработка оконных функций выполняется отдельно для каждого раздела.

`ORDER BY` определяет [порядок](../../overview/glossary.md#ordering), в котором
оконная функция обрабатывает кортежи раздела. Оно работает аналогично `ORDER BY`
на уровне `SELECT`, но также не принимает имена выходных столбцов или числа. Без
`ORDER BY` кортежи обрабатываются в неопределенном порядке.

#### Пример раздела с идущими подряд кортежами {: #example_partition_1}

```sql
SELECT c, a, b, group_concat(b, '.') OVER (
  PARTITION BY c ORDER BY a
  RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
FROM t1
ORDER BY c, a;

   c   | a | b |  col_1
-------+---+---+---------
 one   | 1 | A | "A.D.G"
 one   | 4 | D | "D.G"
 one   | 7 | G | "G"
 three | 3 | C | "C.F"
 three | 6 | F | "F"
 two   | 2 | B | "B.E"
 two   | 5 | E | "E"
```

В этом запросе выражение `PARTITION BY c` разбивает результирующий набор на
три раздела. Первый раздел содержит три кортежа с `c = 'one'`, второй — два
кортежа с `c = 'three'`, третий — два кортежа с `c = 'two'`. В итоговом
результате кортежи каждого раздела идут подряд, так как `PARTITION BY c`
совпадает с началом выражения `ORDER BY` во всем запросе.

Однако это не является обязательным. Кортежи одного раздела могут быть
разбросаны по всему результирующему набору.

#### Пример раздела с разбросанными кортежами {: #example_partition_2 }

```sql
SELECT c, a, b, group_concat(b, '.') OVER (
  PARTITION BY c ORDER BY a
  RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
FROM t1
ORDER BY a;

   c   | a | b |  col_1
-------+---+---+---------
 one   | 1 | A | "A.D.G"
 two   | 2 | B | "B.E"
 three | 3 | C | "C.F"
 one   | 4 | D | "D.G"
 two   | 5 | E | "E"
 three | 6 | F | "F"
 one   | 7 | G | "G"
```

В этом случае кортежи сортируются по `a`, но разделы остаются неизменными,
так как`PARTITION BY` c продолжает делить результат на три независимых
группы.

### Рамки окна {: #frame }

Оконная агрегатная функция считывает кортежи в соответствии с
[рамками](../../overview/glossary.md#frame) окна. Определение рамок включает
три части:

- Тип рамки — `ROWS` или `RANGE`.
- Начальная граница.
- Конечная граница.

Конечную границу можно опустить, если также убрать ключевые слова `BETWEEN`
и `AND`. В этом случае она по умолчанию принимает значение `CURRENT ROW`.

Если тип рамки — `RANGE`, то кортежи с одинаковыми значениями во всех
выражениях `ORDER BY` считаются **равнозначными** (*peers*). Если `ORDER BY`
отсутствует, все кортежи считаются равнозначными. Равнозначные кортежи всегда
находятся внутри одной рамки окна.

По умолчанию оконные агрегатные функции считывают все кортежи от начала
раздела до текущего кортежа включительно, а также его равнозначные кортежи.

```sql
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS
```

Это означает, что кортежи с одинаковыми значениями во всех выражениях
`ORDER BY` будут иметь одинаковый результат оконной функции.

```sql
SELECT a, b, c,
       group_concat(b, '.') OVER (ORDER BY c)
FROM t1 ORDER BY a;

 a | b |   c   |      col_1
---+---+-------+-----------------
 1 | A | one   | "A.D.G"
 2 | B | two   | "A.D.G.C.F.B.E"
 3 | C | three | "A.D.G.C.F"
 4 | D | one   | "A.D.G"
 5 | E | two   | "A.D.G.C.F.B.E"
 6 | F | three | "A.D.G.C.F"
 7 | G | one   | "A.D.G"
```

#### Тип рамки {: #frame_type }

Существует два типа рамок окна: `ROWS` и `RANGE`. Тип рамки определяет,
как вычисляются начальная и конечная границы окна.

##### ROWS {: #rows }

Тип `ROWS` означает, что начальная и конечная границы определяются
путем подсчета отдельных кортежей относительно текущего кортежа.

##### RANGE {: #range }

Тип `RANGE` требует, чтобы в `ORDER BY` окна было ровно одно выражение,
обозначим его как `X`. В этом случае границы рамки определяются на основе
значений `X` во всем разделе. В рамку включаются кортежи, у которых значение
`X` находится в определенном диапазоне относительно значения `X` для
текущего кортежа. Подробнее это описано в [разделе ниже](#frame_boundaries)
про тип границы `PRECEDING`.

Оба типа — `ROWS` и `RANGE` — вычисляют рамки относительно текущего кортежа,
но `ROWS` считает отдельные кортежи, тогда как `RANGE` использует диапазон
значений выражения `X` в `ORDER BY`.

#### Границы рамки {: #frame_boundaries }

Существует пять способов задать начальную и конечную границы рамки окна:

1. `UNBOUNDED PRECEDING`
   Начальная граница — первый кортеж в разделе.

1. `<expr> PRECEDING`
   `<expr>` — неотрицательное числовое выражение. Рамка начинается с кортежа,
   которая находится на `<expr>` позиций перед текущим кортежем. Единицы измерения
   зависят от типа рамки:

   - **`ROWS`** - рамка начинается с кортежа, находящейся на `<expr>` кортежей перед
     текущей, либо с первого кортежа раздела, если таких кортежей меньше `<expr>`.
     `<expr>` должно быть целым числом.

   - **`RANGE`** - `ORDER BY` в окне должен содержать только одно выражение `X`.
     Пусть `Xi` — значение `X` для кортежа `i`, а `Xc` — значение `X` для текущего
     кортежа. Граница рамки определяется так:
     - Если `Xi` или `Xc` нечисловые, граница — первый кортеж, для которой
       `Xi IS Xc` истинно.
     - Если `ORDER BY ASC`, граница — первый кортеж, для которой `Xi >= Xc - <expr>`.
     - Если `ORDER BY DESC`, граница — первый кортеж, для которой `Xi <= Xc + <expr>`.
     - `<expr>` может быть нецелым числом, но должно быть неотрицательной константой.

   Описание границы `"0 PRECEDING"` всегда эквивалентно `"CURRENT ROW"`.

1. `CURRENT ROW`
   Текущий кортеж. В режиме `RANGE` в рамку также входят равнозначные кортежи.

1. `<expr> FOLLOWING`
   Работает аналогично `<expr> PRECEDING`, но рамка начинается на `<expr>` кортежей
   после текущего.

1. `UNBOUNDED FOLLOWING`
   Конечная граница — последний кортеж в разделе.

Конечная граница не может стоять раньше начальной в приведенном выше списке.

##### Пример рамки {: #example_frame }

В этом запросе рамки окна включают все кортежи от текущего до конца набора,
упорядоченного по `ORDER BY a`.

```sql
SELECT c, a, b, group_concat(b, '.') OVER (
  ORDER BY c, a ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
FROM t1 ORDER BY c, a;

   c   | a | b |      col_1
-------+---+---+-----------------
 one   | 1 | A | "A.D.G.C.F.B.E"
 one   | 4 | D | "D.G.C.F.B.E"
 one   | 7 | G | "G.C.F.B.E"
 three | 3 | C | "C.F.B.E"
 three | 6 | F | "F.B.E"
 two   | 2 | B | "B.E"
 two   | 5 | E | "E"
```

В этом примере `ORDER BY c, a` сортирует кортежи, а
`ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING` задает рамки окна от
текущего кортежа до конца раздела.

## Виды функций {: #function_types }

Оконные функции в Picodata бывают двух видов: агрегатные и встроенные. Любая
агрегатная оконная функция может работать как обычная агрегатная,
если опустить выражения `OVER` и `FILTER`. Кроме того, все встроенные агрегатные
функции Picodata могут использоваться как оконные, если добавить к ним
соответствующее выражение `OVER`.

### Агрегатные оконные функции {: #aggregate }

Агрегатная оконная функция похожа на обычную [агрегатную](aggregate.md), за исключением
того, что ее добавление в запрос не изменяет количество возвращаемых кортежей.
Вместо этого для каждого кортежа результат такой функции вычисляется так, как
если бы соответствующая агрегатная функция была применена ко всем кортежам
в рамках окна, определенных выражением `OVER`.

Пример запроса:

```sql
SELECT a, b, group_concat(b, '.') OVER (
  ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) FROM t1;
 a | b |  col_1
---+---+---------
 1 | A | "A.B"
 2 | B | "A.B.C"
 3 | C | "B.C.D"
 4 | D | "C.D.E"
 5 | E | "D.E.F"
 6 | F | "E.F.G"
 7 | G | "F.G"
```

В этом примере рамки окна включают все кортежи между предыдущей
`(1 PRECEDING)` и следующей `(1 FOLLOWING)`, включая текущую. Кортежи
сортируются согласно ORDER BY внутри определения окна (в данном случае
`ORDER BY a`). Например, для кортежей с a=3 окно включает кортежи:
`(2, 'B', 'two')`, `(3, 'C', 'three')`, `(4, 'D', 'one')`. Следовательно,
результат `group_concat(b, '.')` для этого кортежа — `'B.C.D'`.

### Встроенные оконные функции {: #builtin }

Помимо агрегатных оконных функций, Picodata поддерживает
[встроенные](window_builtin.md) оконные функции, которые нельзя использовать
без выражения `OVER`.

Встроенные оконные функции учитывают выражение `PARTITION BY` так же, как и
агрегатные оконные функции: каждый выбранный кортеж относится к своему
разделу, и каждый раздел обрабатывается отдельно. Если `ORDER BY` присутствует
в определении окна, оно влияет на порядок нумерации кортежей.

## Использование общего окна {: #shared_window }

Если несколько оконных функций используют одно и то же определение окна,
можно объявить его один раз с `WINDOW`:

```sql
SELECT row_number() OVER w, SUM(a) OVER w FROM t1 WINDOW w AS ();

 col_1 | col_2
-------+-------
 1     | 28
 2     | 28
 3     | 28
 4     | 28
 5     | 28
 6     | 28
 7     | 28
```
