Oracle. Аналитические функции.

Общий синтаксис для использования аналитических функций следующий

имя_функции(<аргумент>,< аргумент >, . . . )
over (<конструкция_фрагментации><конструкция_упорядочения><конструкция_окна>)


Рассмотрим основные части данного синтаксиса.

1. <конструкция_фрагментации>

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

partition by выражение [, выражение] [, выражение]

Данная конструкция логически разбивает результирующее множество на N групп
по критериям, задаваемым выражениями фрагментации. Аналитические функции применяются к каждой группе независимо, - для каждой новой группы они сбрасываются. Если не указать конструкцию фрагментации, все результирующее множество считается
одной группой.

2. <конструкция_упорядочения>

Конструкция упорядочения имеет следующий синтаксис

order by выражение [asc | desc] [nulls first | nulls last]

Конструкция order by задает критерий сортировки данных в каждой группе (в каждом фрагменте). Это, несомненно, влияет на результат выполнения любой аналитической функции. При наличии (или отсутствии) конструкции order by аналитические функции вычисляются по-другому. Например.
- без конструкции order by

select ename, sal, avg(sal) over ()
from emp




- с конструкцией order by

select ename, sal, avg(sal) over (order by ename)
from emp



Здесь стоит отметить следующее, на самом деле наличие конструкции order by в вызове аналитической функции добавляет стандартную конструкцию окна — RANGE UNBOUNDED PRECEDING. Это означает, что для вычисления используется набор из всех предыдущих и текущей строки в текущем фрагменте. При отсутствии order by стандартным окном является весь фрагмент. То есть по-сути предыдущий запрос будет выглядеть следующим образом

select ename, sal, avg(sal) over (order by ename RANGE UNBOUNDED PRECEDING)
from emp


3. <конструкция_окна>

Конструкция окна позволяет задать перемещающееся или жестко привязанное окно (набор)
данных в пределах группы, с которым будет работать аналитическая функция.
Например, для создания отчета, показывающего сумму зарплат текущего и двух предыдущих сотрудников отдела, можно выполнить следующий запрос

select deptno, ename, sal,
sum(sal) over (partition by deptno order by ename rows 2 preceding) sliding_total
from emp
order by deptno, ename



Можно создавать окна по двум критериям: по диапазону (RANGE) значений данных или по
смещению (ROWS) относительно текущей строки
. Использование конструкции range как было сказано ранее в  некоторых случаях используется неявно, RANGE UNBOUNDED PRECEDING например. Она требует брать все строки вплоть до текущей, в соответствии с порядком, задаваемым конструкцией order by. Следует помнить, что для использования окон
необходимо задавать конструкцию order by.

Окно определяется диапазоном строк, объединяемых в соответствии с заданным порядком.
Применять конструкцию range можно либо с числовыми выражениями (NUMBER), либо с выражениями, значением которого является дата (DATE). Еще одно ограничение для таких окон состоит в том, что в конструкции order by может быть только один столбец — диапазоны по природе своей одномерны. Нельзя задать диапазон в N-мерном пространстве. Пример.
Пусть необходимо выбрать зарплату каждого сотрудника и среднюю зарплату всех принятых на работу в течение 100 предыдущих дней, а также среднюю зарплату всех принятых на работу в течение 100 следующих дней. Соответствующий запрос будет выглядеть так:

select ename, hiredate, sal,
avg(sal) over (order by hiredate asc range 100 preceding) avg_sal_100_days_before,
avg(sal) over (order by hiredate desc range 100 preceding) avg_sal_100_days_after
from emp
order by hiredate desc


Помимо определения окна по диапазону (RANGE), также окна определяются и по количеству строк (ROWS). Для окон по строкам нет ограничений, присущих окнам по диапазону; данные могут быть любого типа и упорядочивать можно по любому количеству столбцов.
Например, пусть нужно вычислить среднюю зарплату для сотрудника и пяти принятых на работу до него и после него. Запрос можно записать следующим образом

select ename, hiredate, sal,
avg(sal) over (order by hiredate asc rows 5 preceding) avg_5_before,
avg(sal) over (order by hiredate desc rows 5 preceding) avg_5_after
from emp
order by hiredate


Зная как определяются окна (по диапазону или по количеству строк), рассмотрим как окончательно задаются окна. В простейшем случае, окно задается с помощью одной из трех следующих взаимоисключающих конструкций.

- UNBOUNDED PRECEDING.
Окно начинается с первой строки текущей группы и заканчивается текущей обрабатываемой строкой.

- CURRENT ROW.
Окно начинается (и заканчивается) текущей строкой.

- Числовое_выражение PRECEDING.
Окно начинается со строки за числовое_выражение строк до текущей, если оно задается по строкам, или со строки, меньшей по значению столбца, упомянутого в конструкции order by, не более чем на числовое выражение, если оно задается по диапазону.

- Числовое_выражение FOLLOWING.
Окно заканчивается (или начинается) со строки, через числовое_выражение строк после текущей, если оно задается по строкам, или со строки, большей
по значению столбца, упомянутого в конструкции order by, не более чем на числовое_выражение, если оно задается по диапазону.

Стоит отметить, что окно CURRENT ROW в простейшем виде, вероятно, никогда не используется, поскольку ограничивает применение аналитической функции одной текущей строкой, а для этого аналитические функции не нужны. В более сложном случае для окна задается также конструкция BETWEEN. В ней CURRENT ROW можно указывать в качестве начальной или конечной строки окна. Начальную и конечную строку окна в конструкции BETWEEN можно задавать с использованием любой из перечисленных выше конструкций. Например, можно  задать  окно  так,  что  обрабатываемая  строка  не  будет
последней, а окажется где-то в середине окна

select ename, hiredate,
first_value(ename) over (order by hiredate asc range between 100 preceding and 100 following),
last_value(ename) over (order by hiredate asc range between 100 preceding and 100 following)
from emp
order by hiredate asc


В данном запросе дополнительно использованы функции first_value() и last_value(), которые возвращают первое значений текущего окна и последнее значение также текущего окна, соответственно, в то время как диапазон окна ограничен слева текущая скользящая дата - 100 дней и справа к текущей скользящей дате + 100 дней, в этом и состоит смысл выражения - between 100 preceding and 100 following.

select
  level,
  count(*) over (order by level asc rows 2 preceding) asc_count,
  count(*) over (order by level desc rows 2 preceding) desc_count
from dual
connect by level <= 10
order by level


Окно rows 2 preceding, как видно из результата запроса, содержит от 1 до 3 строк (это определяется тем, как далеко текущая строка находится от начала группы). Для первой строки группы имеем значение 1 (предыдущих строк нет). Для следующей строки в группе таких строк 2. Наконец, для третьей и далее строк значение count(*) остается постоянным, поскольку мы считаем только текущую строку и две предыдущие.


select
  n,
  sum(n) over (order by days range 2 preceding) n_sum,
  days
from (
  select
     level n,
     ( to_date('10.01.2014', 'dd.mm.yyyy') + (level - 1) ) days
  from dual
  connect by level <= 10
)
order by days


В данном случае идет суммирование в пределах окна, диапазон которого составляет скользящий день и 2 предыдущих дня


select
  ename,
  sal,
  rank() over (order by sal) rank,
  dense_rank() over (order by sal) dens_rank,
  row_number() over (order by sal) row_number
from emp
order by sal


Данный запрос демонстрирует работу ранжирующих функций rank(), dense_rank() и row_number() по окладам работников. Обратите внимание на поведение данных функций в строках с одинаковыми значениями окладов.


select
  ename,
  deptno,
  sal,
  rank() over (partition by deptno order by sal) rank,
  dense_rank() over (partition by deptno order by sal) dens_rank,
  row_number() over (partition by deptno order by sal) row_number
from emp
order by deptno


Можно предыдущий запрос фрагментировать по отделам, то есть разбить на группы по отделам, в пределах которых будут работать аналитические функции, которые при выходе за пределы групп будут сбрасывать результаты. Результат представлен ниже



Рассмотрим пример применения функций lag() и lead(). Для того, чтобы можно было обращаться в текущей строке к предыдущим строкам, необходимо использовать функцию - lag(). Синтаксис ее таков

lag(поле_для_обращения, смещение, значение_для_замещения_null
over (partition by выражение order by выражение)

поле_для_обращения - поле, по которому нужно просматривать значения;
смещение - смещенная строка, с которой просматривается поле, по-умолчанию равно 1, если проставить 0, тогда будет просматриваться текущее поле;
значение_для_замещения_null - по-умолчанию равно null, в случае отсутствия значения в просматриваемом поле, возвращает данное значение. Здесь стоит отметить, что подставляемое значение должно быть того же типа, что и просматриваемое поле;
для данной функции обязательно использование order by

with
  main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from emp
    order by sal
  ),
  numerated_main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from main
  )
select
  ename,
  sal,
  lag(sal) over (order by rownum) previous_sal
from numerated_main



Или вот так

with
  main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from emp
    order by sal
  ),
  numerated_main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from main
  )
select
  ename,
  sal,
  lag(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main




Логика и синтаксис работы функции lead() аналогичен предыдущей функции с одной лишь разницей: просмотр идет не назад, а вперед

with
  main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from emp
    order by sal
  ),
  numerated_main as (
    select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
    from main
  )
select
  ename,
  sal,
  lead(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main


Ниже для ознакомления приведен неполный список аналитических функций. Для более полной информации обращайтесь к документации. Всем успехов.

Аналитическая функцияОписание
AVG([DISTINCT | ALL] выражение)Используется  для  вычисления  среднего значения выражения  в  пределах  группы  и окна.  Для  поиска среднего  после  удаления  дублирующихся  значений  можно
указывать ключевое слово DISTINCT
CORR(выражение, выражение)Выдает  коэффициент  корреляции  для  пары  выражений, возвращающих  числовые  значения. В  статистическом  смысле,  корреляция  —  это  степень
связи  между  переменными.  Связь  между переменными означает,  что  значение  одной  переменной  можно  в определенной степени предсказать по значению другой. Коэффициент  корреляции  представляет степень корреляции  в  виде  числа  в  диапазоне  от  -1  (высокая обратная корреляция) до 1 (высокая корреляция).
Значение 0 соответствует отсутствию корреляции
COUNT([DISTINCT][*] [выражение])Эта  функция  считает  строки  в  группах.  Если  указать  * или  любую  константу,  кроме  NULL,  функция  COUNT  будет считать  все  строки.  Если  указать  выражение,  функция COUNT  будет  считать  строки,  для  которых  выражение имеет  значение  не  NULL.  Можно  задавать  модификатор DISTINCT,  чтобы  считать  строки  в  группах  после удаления дублирующихся строк
COVAR_POP(выражение, выражение)Возвращает  ковариацию  генеральной  совокупности (population  covariance)  пары  выражений  с  числовыми значениями
COVAR_SAMP(выражение, выражение)Возвращает  выборочную  ковариацию  (sample  covariance) пары выражений с числовыми значениями
CUME_DISTВычисляет относительную позицию строки в группе. Функция CUME_DIST всегда возвращает число большее  0 и меньше или равное  1. Это число представляет "позицию" строки  в  группе  из  N  арок.  В  группе  из  трех  строк, например,  возвращаются  следующие  значения кумулятивного распределения: 1/3, 2/3 и 3/3
DENSE_RANKЭта  функция  вычисляет  относительный  ранг  каждой возвращаемой  запросом  строки  по  отношению  к  другим строкам,  основываясь  на  значениях  выражений  в конструкции  ORDER  BY.  Данные  в  группе  сортируются  в соответствии  с  конструкцией  ORDER  BY,  а  затем  каждой строке  поочередно  присваивается  числовой  ранг, начиная с 1. Ранг увеличивается при каждом изменении значений выражений,  входящих  в  конструкцию  ORDER  BY.  Строки  с одинаковыми  значениями  получают  один  и  тот  же  ранг (при  этом  сравнении  значения  NULL  считаются одинаковыми).  Возвращаемый  этой  функцией  "плотный" ранг дает ранговые значения без промежутков. Сравните с представленной далее функцией RANK
FIRST_VALUEВозвращает первое значение в группе

LAG(выражение,<смещение>,
<стандартное
значение>)
Функция  LAG  дает  доступ  к  другим  строкам результирующего  множества,  избавляя  от  необходимости выполнять  самосоединения.  Она  позволяет  работать  с курсором  как  с  массивом.  Можно  ссылаться  на  строки, предшествующие  текущей  строке  в  группе.  О  том,  как обращаться  к  следующим  строкам  в  группе,  см.  в описании функции LEAD. Смещение  -  это  положительное  целое  число  со стандартным значением 1 (предыдущая строка). Стандартное  значение  возвращается,  если  индекс выходит  за  пределы  окна  (для  первой  строки  группы будет возвращено стандартное значение)
LAST_VALUEВозвращает последнее значение в группе
LEAD(выpaжeниe,<смещение>,<стандартное
значение>)
Функция  LEAD  противоположна функции  LAG. Если функция LAG  дает  доступ  к  предшествующим  строкам  группы,  то функция  LEAD  позволяет  обращаться  к  строкам, следующим за текущей. Смещение  —  это  положительное  целое  число  со стандартным  значением  1  (следующая  строка). Стандартное  значение  возвращается,  если  индекс выходит  за  пределы  окна  (для  последней  строки  группы будет возвращено стандартное значение)
МАХ(выражение)
Находит  максимальное  значение  выражения  в  пределах окна в группе
МIN(выражение)Находит  минимальное  значение  выражения  в  пределах окна в группе
NTILE(выражение)Делит группу на фрагменты по значению выражения. Например,  если  выражение  =  4,  то  каждой  строке  в группе присваивается число от 1 до 4 в соответствии с фрагментом,  в  которую  она  попадает.  Если  в  группе  20 строк,  первые  5  получат  значение  1,  следующие  5  — значение  2  и  т.д.  Если  количество  строк  в  группе  не делится  на  значение  выражения  без  остатка,  строки распределяются  так,  что  ни  в  одном  фрагменте количество  строк  не  превосходит  минимальное количество в других фрагментах более чем на 1, причем дополнительные  строки  будут  в  группах  с  меньшими номера фрагмента. Например,  если  снова  выражение  =  4,  а  количество строк  =  21,  в  первом  фрагменте  будет  6  строк,  во втором и последующих - 5
PERCENT RANKАналогична  функции  CUME_DIST  (кумулятивное распределение).  Вычисляет  ранг  строки  в  группе  минус 1,  деленный  на  количество  обрабатываемых  строк  минус 1. Эта функция всегда возвращает значения в диапазоне от 0 до 1 включительно
RANKЭта  функция  вычисляет  относительный  ранг  каждой строки,  возвращаемой  запросом,  на  основе  значений выражений,  входящих  в  конструкцию  ORDER  BY.  Данные  в группе  сортируются  в  соответствии  с  конструкцией ORDER  BY,  а  затем  каждой  строке  поочередно присваивается  числовой  ранг,  начиная  с  1.  Строки  с одинаковыми  значениями  выражений,  входящих  в конструкцию  ORDER  BY,  получают  одинаковый  ранг,  но если  две  строки  получат  одинаковый  ранг,  следующее значение ранга пропускается. Если две строки получили ранг  1,  строки с рангом  2  не будет;  следующая строка в  группе  получит  ранг  3.  В  этом  отличие  от  функции DENSE_RANK, которая не пропускает значений
RATIO_TO_REPORT(выражение)Эта  функция  вычисляет  значение  выражение  / (SUM(выражение)) по строкам группы. Это дает процент,  который составляет значение текущей строки по отношению к SUM(выражение)
REGR_xxxxxxx(выражение,выражение)Эти  функции  линейной  регрессии  применяют  стандартную
линейную  регрессию  по  методу  наименьших  квадратов  к
паре  выражений.  Предлагается  9  различных  функций регрессии
ROW_NUMBERВозвращает  смещение  строки  по  отношению  к  началу
упорядоченной  группы.  Может  использоваться  для последовательной  нумерации  строк,  упорядоченных  по определенным критериям
STDDEV(выражение)Вычисляет  стандартное  (среднеквадратичное)  отклонение
(standard  deviation)  текущей  строки  по  отношению  к группе
STDDEV_POP(выражение)Эта  функция  вычисляет  стандартное  отклонение генеральной  совокупности  (population  standard deviation)  и  возвращает  квадратный  корень  из дисперсии  генеральной  совокупности  (population variance).  Она  возвращает  значение,  совпадающее  с квадратным корнем из результата функции VAR_POP
STDDEV_SAMP(выражение)Эта  функция  вычисляет  накопленное  стандартное отклонение  выборки  (cumulative  sample  standard deviation)  и  возвращает  квадратный  корень  выборочной дисперсии  (sample variance). Она возвращает значение, совпадающее  с квадратным корнем из результата функции
VAR_SAMP
SUM(выражение)Вычисляет общую сумму значений  выражения для группы
VAR_POP(выражение)Эта  функция  возвращает  дисперсию  генеральной совокупности  для  набора  числовых  значений  (значения NULL игнорируются)
VAR_SAMP(выражение)Эта  функция  возвращает  выборочную  дисперсию  длянабора  числовых  значений  (значения  NULL игнорируются)
VARIANCE(выражение)Возвращает  дисперсию  для  выражения.  Сервер  Oracle вычисляет дисперсию как:
- 0, если количество строк в группе = 1;            -VAR_SAMP, если количество строк в группе больше 1