【问题标题】:Analytic Function SUM returns Average for window with same values分析函数 SUM 返回具有相同值的窗口的平均值
【发布时间】:2018-12-14 12:34:40
【问题描述】:

我正在尝试编写一个查询,该查询会产生给定值的运行总和。但是,当应用 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;

结果:

我真正想要的是

我使用 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;

第一个结果中显示的 peter 的 running_sum 实际上是 peter 的总 running_sum 的平均值。分析函数正在为彼得考虑一个窗口,因为我在窗口子句中包含了“NAME”。但是为什么查询结果是窗口的平均值而不是运行 sum?

【问题讨论】:

  • 你为什么认为这些是平均值,以及什么?它们是总和……?按名称排序,或者至少只是按名称排序, in 并没有什么意义——因为 peter 的所有三行排序相同。是否还有其他一些您没有显示的字段(例如时间戳)决定了它们的评估顺序?
  • 我只能猜测有一个隐含的 GROUP BY 因为您使用的是聚合函数,首先按名称,在第二种情况下按 rownum

标签: sql oracle analytic-functions


【解决方案1】:

为什么查询结果是窗口的平均值而不是求和?

这不是窗口的平均值。在您的 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

【讨论】:

  • 我关于平均值的逻辑有缺陷。
【解决方案2】:

检查我的答案。

我为 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;

【讨论】:

    【解决方案3】:

    所以,实际发生的是,分析函数首先聚合一个窗口的总数,在本例中为 peter,即 1080,然后将结果添加到前面的行总数中,即 8999.5,最后显示总数(1080 + 8999.5 = 10079.5) 对于窗口中的每一行。

    与窗口的平均值无关。

    【讨论】:

    • 只是总结了一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    • 2023-04-01
    • 1970-01-01
    相关资源
    最近更新 更多