【问题标题】:Case With Aggregate Function in Oracle SqlOracle Sql 中的聚合函数案例
【发布时间】:2019-12-08 17:13:21
【问题描述】:

我有一个包含用户每月出勤详细信息的表格,其中有一个加班列。因此该表具有以下结构和示例数据:

Date      - Id   - Dept - OT - Status
12/1/2019 - 1001 - HR   - 2  - P
12/2/2019 - 1001 - HR   - 4  - P 
12/1/2019 - 1002 - IT   - 2  - P
12/2/2019 - 1002 - IT   - 4  - P 

我在这里省略了进出时间,因为这不是这里的主要问题。如上所示,加班时间是为每个用户计算的。因此很容易计算出一个用户或部门的总加班详情如下:

SELECT DEPT, SUM(OT) TOTAL FROM ATTENDANCE m WHERE M.DATE >= '01-DEC-2019' AND M.DATE <= '10-DEC-2019'
GROUP BY M.DEPT

我的要求是获取具有特定日期范围的部门的每周或 7 天加班详细信息,加班范围为 40。例如:对于一个部门,日期范围为 2019 年 12 月 1 日至 7 日总加班时间最长为 40 小时或 41 至 50 小时。所以我尝试了以下方法:

SELECT DEPT, SUM(OT) TOTAL FROM ATTENDANCE m WHERE M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
HAVING SUM(OT) <= 40 GROUP BY M.DEPT

或者:

SELECT DEPT, SUM(OT) TOTAL FROM ATTENDANCE m WHERE M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
HAVING SUM(OT) BETWEEN 41 AND 50 GROUP BY M.DEPT

或者:

SELECT DEPT, SUM(OT) TOTAL FROM ATTENDANCE m WHERE M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
HAVING SUM(OT) > 50 GROUP BY M.DEPT

但最后一个查询似乎返回了奇怪的输出,并且似乎返回了我在第一个查询中检索到的数据。这些是我厌倦的单独查询,但我正在考虑是否可以使用子查询将其与CASE WHEN 一起使用,上面给出的所有范围如下:

CASE WHEN M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
AND SUM(OT) <= 40 THEN SUM(OT) 
CASE WHEN M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
AND SUM(OT) BETWEEN 41 AND 50 THEN SUM(OT) 

我不确定聚合函数是否可以与 CASE WHEN 语句一起使用,或者我可以使用上述任何方式在查询中运行多个范围?谢谢。

【问题讨论】:

  • 不再阅读,先试试这个...不要将日期与字符串进行比较。而不是'01-DEC-2019',而是写to_date('01-DEC-2019', 'DD-MON-YYYY')(和其他地方类似),看看查询是否按需要工作。

标签: sql oracle oracle10g


【解决方案1】:

您能否更明确地说明您想要的输出是什么?您正在寻找两个结果(最多 40 小时加班的部门,以及在给定日期超过 40 小时加班的部门范围)?

查看您的最后一个查询:

CASE WHEN M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
AND SUM(OT) <= 40 THEN SUM(OT) 
CASE WHEN M.DATE >= '01-DEC-2019' AND M.DATE <= '07-DEC-2019'
AND SUM(OT) BETWEEN 41 AND 50 THEN SUM(OT) 

鉴于应用的日期范围是相同的,您可以将其移至WHERE 子句吗?然后,您可以使用子查询或其他方法根据已使用的加班时间对部门进行分组(类似这样):

SELECT
    Dept
    , CASE
        WHEN TotalOvertime <= 40 THEN 'something'
        WHEN (TotalOvertime > 40 AND <= 50) THEN 'something_else'
        ELSE 'exception'
    END AS OvertimeCaseStatement
FROM (
SELECT Dept
    , SUM(OT) AS TotalOvertime
FROM Attendance
WHERE Date BETWEEN TO_DATE('01-DEC-2019', 'DD-MON-YYYY') AND TO_DATE('07-DEC-2019', 'DD-MON-YYYY')
GROUP BY Dept
)
;

我还删除了 int 比较中的 BETWEEN 运算符,因为我认为使用逻辑运算符 bit 更清晰(BETWEEN 是一个包容性运算符),并添加了一些不好的名称如somethingsomething_else 等,您可以更改为更具描述性的名称。

如果我正在解决您的实际问题,以及这种方法是否存在新的错误/问题,请告诉我!

FWIW,如果您的 Date 列是日期而不是字符串,则将 CAST 您的参数('01-DEC-2019' 等)设置为日期类型可能会有所帮助。希望这会有所帮助!

已编辑根据@mathguy 的良好调用添加了TO_DATE() 的日期转换(我假设Date 已经是一个日期类型col)。

【讨论】:

  • 在这里,我可以写这个吗 - WHEN TotalOvertime &lt;= 40 THEN 'something' to this - WHEN TotalOvertime &lt;= 40 THEN TotalOvertime or SUM(OT)
  • 我不知道我完全理解你在问什么——你希望有一个聚合函数作为CASE 语句的结果吗?根据您对整个查询的期望输出,可能有一种更简单的方法来获得您想要的结果
【解决方案2】:

您可以使用CASE 区分所有值。例如:

select 
  dept, 
  sum(ot) as total,
  case when sum(ot) <= 40 then 'g1'
       when sum(ot) > 40 and sum(ot) <= 50 then 'g2'
       else 'g3' end as g
from attendance
where between to_date('2019-12-01', 'yyyy-mm-dd') 
          and to_date('2019-12-07', 'yyyy-mm-dd')
group by dept

【讨论】:

  • @mathguy 好的,不是重点,但修复了代码以在 Oracle 中工作。另外使用了 ISO 日期格式,去掉了英文格式。
  • “不是重点”是正确的,但仍然 - 如果我们教某人如何做代数,我们不允许他们除以零,即使这不是他们所要求的。不管怎样,你修好了,所以我删除了反对票。这就是它应该如何工作的方式。我不会反对,但两个答案(都使用字符串作为日期)发布之后我已经在他的问题下的评论中向 OP 指出了这一点。
猜你喜欢
  • 1970-01-01
  • 2014-11-16
  • 2018-12-09
  • 2014-05-09
  • 2018-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
相关资源
最近更新 更多