Comments 54
Оконные функции удобно применять для всякой аналитики, отчетов, и так далее.
А еще однажды была задача добавить unique constraint на таблицу, где уже было порядка 500к записей и из них много не соответствовало условию. Запрос на удаление лишних записей был удобно сформирован с помощью оконной фукнции. Боюсь представить, сколько времени я бы удалял это вручную.
если в последних версиях конечно не добавили что то типа offset
Добавили technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
SELECT First Name + ' ' + Last Name FROM Employees ORDER BY First Name OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;
Вот такая особенность агрегатных функций, если их использовать как оконные. На мой взгляд, это довольно-таки странный, интуитивно неочевидный момент SQL-стандарта.
Не понял, чего тут не очевидного? Не указываем
order by
— границами окна являются первая и последняя запись в выборке, указываем — первая и текущая запись в выборке.Кстати, ваше описание оконных функций неполно — как минимум не указаны полезнейшие предложения
range
/ rows
, позволяющие явно указать границы окна.Если мы берем функцию row_number(), то она как-то все равно учитывает текущую строку, даже если не делать order by. А sum — никак не учитывает. Вот это и не очевидно, на мой взгляд.
«When an aggregate function is used as a window function, it aggregates over the rows within the current row's window frame. An aggregate used with ORDER BY and the default window frame definition produces a „running sum“ type of behavior, which may or may not be what's wanted. To obtain aggregation over the whole partition, omit ORDER BY or use ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. Other frame specifications can be used to obtain other effects.»
Представим, что есть таблица со списком тарифов, для каждого тарифа есть цена и дата начала действия. Текущую цену можно будет узнать таким запросом.
+----+------------+--------+------------+
| id | service_id | tariff | put_into |
+----+------------+--------+------------+
| 1 | 1 | 100.0 | 2013-10-01 |
| 2 | 1 | 200.0 | 2014-12-01 |
| 3 | 1 | 150.0 | 2015-02-01 |
| 4 | 1 | 170.0 | 2016-02-15 |
| 5 | 2 | 300.0 | 2013-10-01 |
| 7 | 2 | 330.0 | 2015-02-01 |
| 8 | 2 | 315.0 | 2016-02-15 |
+----+------------+--------+------------+
select distinct first_value (t1.tariff) over (order by t1.put_into desc) as price
from tariff_price t1 where t1.service_id = 1 and t1.put_into <= CURRENT_DATE;
+-------+
| price |
+-------+
| 150.0 |
+-------+
select t1.tariff from from tariff_price t1 where t1.service_id = 1 and t1.put_into <= CURRENT_DATE order by t1.put_into desc limit 1
И очевиднее и план запроса куда проще.
Если же нужно найти последние цены по всем видам услуг — то не подойдет :)
Прошу в коментариях накидать примеров, где особенно удобно применять оконные фунции. А также, какие с ними могут возникнуть проблемы, если таковые имеются.
Ну я и дал пример сферического запроса в вакуме, реальный запрос намного сложнее и масштабнее, но я его здесь приводить не буду так как не интересно.
select distinct on (t1.service_id) t1.service_id, t1.tariff from from tariff_price t1 where t1.put_into <= CURRENT_DATE order by t1.service_id, t1.put_into desc
http://sqlfiddle.com/#!15/6bba4/1
Как следствие — при обновлении строки обновляется ссылка на данные в индексе
Вот простой пример расчёта среднего по умолчанию
SELECT
NAME,
SALARY,
AVG(SALARY) OVER (ORDER BY SAL) AVG
FROM
EMP;
NAME SALARY AVG
---------- ---------- ----------
SMITH 800 800
JAMES 950 875
ADAMS 1100 950
WARD 1250 1025
Подробнее, например, на www.dba-oracle.com/t_advanced_sql_windowing_clause.htm
select id,
sum(viewed) FILTER (where viewed > 5) OVER (ORDER BY expectedenddate DESC) AS rating
from table1
Если добавлено предложение FILTER, агрегатной функции подаются только те входные строки, для которых условие_фильтра вычисляется как истинное; другие строки отбрасываютсяНу и доп. ссылки:PS: спецом на postgrespro.ru ссылки поставил, уж больно Олег Бартунов его хвалил в Уфе
val avg
3 | 4.5
6 | 4.5
3 | 4
5 | 4.5
4 |
Бывают. Для этого как раз предназначены фразы range
/ rows
оконной функции. На примере Oracle:
with data as (
select 3 val from dual union all
select 6 val from dual union all
select 3 val from dual union all
select 5 val from dual union all
select 4 val from dual
)
select n, val, avg(val) over(order by n rows between current row and 1 following) avg
from (select rownum n, val from data) t;
Результат:
| N | VAL | AVG |
|---|-----|-----|
| 1 | 3 | 4.5 |
| 2 | 6 | 4.5 |
| 3 | 3 | 4 |
| 4 | 5 | 4.5 |
| 5 | 4 | 4 |
Здесь колонка n
искусственно введена для того, чтобы правильно упорядочить данные во фразе order by
окна. Если у вас уже есть колонка сортировки, эта искусственная колонка не нужна. Фраза rows between current row and 1 following
определяет границы окна: между текущей строкой и плюс одной строкой от текущей (включительно) — получаем ваш интервал в 2 строки; rows
означает, что оперируем физическими строками. Значения за последней строкой считаются как бы равными null
. Ещё можно использовать range
— в этом случае диапазон будет рассчитываться не от порядковых номеров строк, а от значений в колонке n
(той, которая в order by
). В нашем случае порядковый номер и значение совпадают, так что результат был бы одним и тем же, но если бы вместо n
стоял, например, реальный идентификатор строки, разница была бы существенной!
Документация по аналитическим функциям.
Некропост, но ... вот пример, где удобно использовать оконные функции: в БД есть некоторая таблица MyTable. Все строки MyTable соответствующие критерию a образуют множество строк A, а все строки соответствующие критерию b образуют множество строк В. Необходимо получить объединение множеств A и B, при этом строки выводятся в следующем порядке:
Сперва все строки соответствующие только критерию a.
Затем все строки соответствующие критерию a и критерию b.
Под конец все строки соответствующие только критерию b. Критерии a и b не определенны заранее и формируются динамически.
можете привести пример? Не оч понятно
Ок. Пусть у нас есть таблица:
create table MyTable (
id UUID not null,
value1 INT not null,
value2 INT not null,
primary key(id)
);
Нужно вывести все строки которые соответствуют критерию value1 > 1000 или критерию value2 > -250 and value2 < 370, при этом строки должны быть отсортированы в следующем порядке:
Сперва все строки соответствующие только критерию value1 > 1000
Затем все строки соответствующие критерию value1 > 1000 и критерию value2 > -250 and value2 < 370
Под конец выводятся строки соответствующие только критерию value2 > -250 and value2 < 370
Как посчитать всё на свете одним SQL-запросом. Оконные функции PostgreSQL