Аналитическая функция SUM возвращает Среднее для окна с такими же значениями - PullRequest
0 голосов
/ 14 декабря 2018

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

Пример: рассмотрим следующий запрос:

with tbl as 
(
select 'steve' "NAME", 2000 val from dual UNION ALL
select 'john' "NAME", 4000 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 90 val from dual UNION ALL
select 'peter' "NAME", 450 val from dual UNION ALL
select 'hary' "NAME", 2772 val from dual UNION ALL
select 'may' "NAME", 2227.5 val from dual UNION ALL
select 'tom' "NAME", 500 val from dual UNION ALL
select 'sia' "NAME", 20000 val from dual
)
select name, val,
        sum(val) over (order by name) running_sum
from tbl;

Результат:

enter image description here

Что я действительно хотел, так это

enter image description here

Что я получил, используя ROWNUM:

with tbl as 
(
select 'steve' "NAME", 2000 val from dual UNION ALL
select 'john' "NAME", 4000 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 90 val from dual UNION ALL
select 'peter' "NAME", 450 val from dual UNION ALL
select 'hary' "NAME", 2772 val from dual UNION ALL
select 'may' "NAME", 2227.5 val from dual UNION ALL
select 'tom' "NAME", 500 val from dual UNION ALL
select 'sia' "NAME", 20000 val from dual
)
select name, val,
        sum(val) over (order by rownum) running_sum
from tbl;

Параметр running_sum для Питера, отображаемый в первом результате, фактически является средним значением от общего значения running_sum для Питера.Аналитическая функция рассматривает окно для Питера, так как я включил «NAME» в предложении windowing.Но почему запрос приводит к среднему значению для окна, а не к бегущей сумме?

Ответы [ 3 ]

0 голосов
/ 14 декабря 2018

почему запрос приводит к среднему значению для окна вместо текущей суммы?

Это не среднее значение для окна.В вашей версии rownum показаны четыре значения: 6270, 6540, 6630 и 7080, что в среднем составляет 6630.

Это текущая сумма, но, возможно, не совсем то, что вы ожидали, и порядоквывод, который отображается, скрывает логику, которую вы на самом деле применяете.

Вы можете увидеть, откуда приходят цифры, которые вы видите, упорядочив вывод:

with tbl as 
(
select 'steve' "NAME", 2000 val from dual UNION ALL
select 'john' "NAME", 4000 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 90 val from dual UNION ALL
select 'peter' "NAME", 450 val from dual UNION ALL
select 'hary' "NAME", 2772 val from dual UNION ALL
select 'may' "NAME", 2227.5 val from dual UNION ALL
select 'tom' "NAME", 500 val from dual UNION ALL
select 'sia' "NAME", 20000 val from dual
)
select name, val,
        sum(val) over (order by name) running_sum
from tbl
order by name;

NAME         VAL RUNNING_SUM
----- ---------- -----------
hary        2772        2772
john        4000        6772
may       2227.5      8999.5
peter        450     10079.5
peter        270     10079.5
peter        270     10079.5
peter         90     10079.5
sia        20000     30079.5
steve       2000     32079.5
tom          500     32579.5

ВыМожно видеть, что промежуточные итоги теперь имеют смысл, исходя из того, как они оцениваются вашим предложением о работе с окнами.Все четыре значения для peter включены в промежуточный итог для каждой из этих строк - потому что это все, что есть в order by - и что общее количество 450 + 270 + 270 + 90 = 1080 добавляется к итогу предыдущего именииз 8999.5.

Вы можете получить разные значения для каждой строки peter, включив предложение окна на основе строки:

with tbl as 
(
select 'steve' "NAME", 2000 val from dual UNION ALL
select 'john' "NAME", 4000 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 90 val from dual UNION ALL
select 'peter' "NAME", 450 val from dual UNION ALL
select 'hary' "NAME", 2772 val from dual UNION ALL
select 'may' "NAME", 2227.5 val from dual UNION ALL
select 'tom' "NAME", 500 val from dual UNION ALL
select 'sia' "NAME", 20000 val from dual
)
select name, val,
        sum(val) over (order by name
          rows between unbounded preceding and current row) running_sum
from tbl;

NAME         VAL RUNNING_SUM
----- ---------- -----------
hary        2772        2772
john        4000        6772
may       2227.5      8999.5
peter        450      9449.5
peter        270      9719.5
peter        270      9989.5
peter         90     10079.5
sia        20000     30079.5
steve       2000     32079.5
tom          500     32579.5

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

Весь результат теперь неявно упорядочен (по крайней мере сегодня, с использованием CTE и с моей версией и решениями моего оптимизатора), чтоможет быть не то, что вы хотите;но тогда вы должны иметь явное order by в любом случае, если порядок имеет значение для вас, что бы это ни было.

0 голосов
/ 14 декабря 2018

Итак, на самом деле происходит то, что аналитическая функция сначала агрегирует итоговое значение для окна, в данном случае для Петра, равное 1080, затем добавляет результат к сумме предыдущих строк, 8999,5, и, наконец, отображает итоговое значение (1080 +8999,5 = 10079,5) для каждой строки в окне.

Нет ничего общего со средним значением для окна.

0 голосов
/ 14 декабря 2018

проверьте мой ответ.

Я добавил еще один подзапрос для row_number и отсортировал по нему

with tbl as 
(
select 'steve' name, 2000 val from dual UNION ALL
select 'john' "NAME", 4000 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 270 val from dual UNION ALL
select 'peter' "NAME", 90 val from dual UNION ALL
select 'peter' "NAME", 450 val from dual UNION ALL
select 'hary' "NAME", 2772 val from dual UNION ALL
select 'may' "NAME", 2227.5 val from dual UNION ALL
select 'tom' "NAME", 500 val from dual UNION ALL
select 'sia' "NAME", 20000 val from dual
), tbl2 as
(
  select row_number() over (order by 1) rn, tbl.* from tbl
)
select name, val,
        sum(val) over (order by rn) running_sum
from tbl2;
...