Oracle 运行总计

Posted

技术标签:

【中文标题】Oracle 运行总计【英文标题】:Oracle Running Total 【发布时间】:2012-07-09 18:20:18 【问题描述】:

使用 PLSQL 寻找具有 2 种不同类型小计的建议。

我需要提取一个包含 1) 唯一人数和 2) 学分总数的数据集,作为一段时间内的运行总数。

原始数据: 这是交易数据——每次学生注册或课程时,都会插入一条记录,其中包含日期、学生 ID 和学分(以及课程编号和一堆其他相关数据)。每个学生每门课程一条记录。

STUDENT_ID   CREDITS   DATE
1            3         01-JAN-12
1            2         02-JAN-12
57           1         03-JAN-12
1            1         03-JAN-12

处理过的数据: 这是老板需要看到的——它将用于稍后的趋势(例如,查看今年的 Jan-01 与去年 Jan-01 的对比情况等)。

UniqueHeadcount   SumCredits   Date
1                 3            01-JAN-12
1                 5            02-JAN-12
2                 7            03-JAN-12

对此的粗略方法是编写一堆单独的 SELECTS(每天一个),然后将它们联合在一起。例如:

SELECT
  COUNT(DISTINCT STUDENT_ID) as "UniqueHeadcount",
  SUM(CREDIT_HR) as "SumCredits",
  '01-JAN-12' as "DATE"
FROM
  REGISTRATIONS
WHERE
  TO_CHAR(DATE,'yyyymmdd') <= '20120101' 
GROUP BY
  '01-JAN-12'

UNION

SELECT
  COUNT(DISTINCT STUDENT_ID) as "UniqueHeadcount",
  SUM(CREDIT_HR) as "SumCredits",
  '02-JAN-12' as "DATE"
FROM
  REGISTRATIONS
WHERE
  TO_CHAR(DATE,'yyyymmdd') <= '20120102' 
GROUP BY
  '02-JAN-12'

UNION

...

这行得通——结果是准确的——但正如你所见——这远非优雅——如果你必须这样做 365 天,那么......这简直就是野兽。必须有更好的方法来做到这一点。

到目前为止,在我的搜索中,我已经了解了一个可以使用的“OVER”子句——就像这样:

SELECT
  COUNT(DISTINCT STUDENT_ID) OVER(ORDER BY TRUNC(RSTS_DATE) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "UniqueHeadcount",
  SUM(CREDIT_HR) OVER(ORDER BY TRUNC(RSTS_DATE) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as "SumCredits",
  TRUNC(RSTS_DATE) as "DATE"
FROM
  REGISTRATIONS

这个查询要短得多(耶)——但有两个我还没有找到解决办法的重大问题。首先是它对 COUNT DISTINCT 不起作用(显然是设计使然?)。所以我评论了一会儿,然后遇到了第二个问题:它忽略了 TRUNC() 函数。 RSTS_DATE,虽然当您在其上运行 SELECT 时它似乎只是一个日/月/年的值,但实际上也包含时间,所以我得到的结果集不是简单地在日期上求和,而是随着时间的推移——因此,我处理的数据不是每天一条记录,而是每天返回数百条记录(每个单独的课程注册一条)。例如:

UniqueHeadcount   SumCredits   Date
1                 3            01-JAN-12
1                 5            02-JAN-12
2                 6            03-JAN-12 (hidden time: 07:32:27)
2                 7            03-JAN-12 (hidden time: 08:01:33)

不是我想要的。

所以我正在寻找专业知识——如果我到目前为止所解释的内容有意义的话——是否有另一种使用 OVER 子句的方法,或者我应该为此使用 PLSQL 的另一个特性?如果你不知道,我在 PLSQL 方面并不强,但如果有人能给我一些指导——即使只是给谷歌的话,我会很感激你的帮助。

谢谢

【问题讨论】:

【参考方案1】:

试试这个:

WITH CRdata AS
(
    SELECT COUNT(DISTINCT STUDENT_ID) AS UniqueHeadcount,
    SUM(CREDIT_HR)                     AS SumCredits,
    TRUNC(RSTS_DATE) RSTS_DATE
     FROM REGISTRATIONS
    GROUP BY TRUNC(RSTS_DATE)
)
 SELECT SUM(UniqueHeadcount) OVER(ORDER BY RSTS_DATE ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS UniqueHeadcount,
  SUM(SumCredits) OVER(ORDER BY RSTS_DATE ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS  SumCredits, 
  RSTS_DATE 
  FROM CRdata

【讨论】:

谢谢——问题在于,它只计算每个日期的总和。我需要它做的是对截至该日期的每条记录进行计数和求和(即,运行总数,而不是每日总数)。这有意义吗? 越来越近了。这对于运行学分总数非常有用(这比我能够取得的进步更多),但仍然只返回每日总人数以获得独特的员工人数。不过我会尝试一下,看看我是否可以修改它。 @tamago 更新了答案。请检查。这是 SqlFiddle:sqlfiddle.com/#!4/c694d/4 -1 你不能把两个count(distinct X)相加并假设两者的总和仍然是一个count(distinct)。由于 X 集合的交集,您的总和不再是计数(不同)。

以上是关于Oracle 运行总计的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL 分析查询 - 类似电子表格的递归运行总计

Oracle SQL 在字段更改时运行总计(仅在字段更改时对列求和)

oracle中如何添加小计和总计

在 Oracle 中计算并添加多列的总计行

Oracle 中总计和小计使用的函数

Oracle SQL Pivot -- 获取行总计