GROUP BY 两列,并根据 SQL 中的条件返回第三列值

GROUP BY two columns and return a third column values based on condition in SQL

提问人:basrood 提问时间:11/6/2023 更新时间:11/7/2023 访问量:43

问:

我有一些数据由date_time、job_name和状态列组成。这些作业可以每天运行一次,也可以每天在不同时刻运行多次,它们会导致“成功”、“失败”或“禁用”状态。

我的数据如下所示:

date_time job_name 地位
01/01/2020 07:30:30 job_1 成功
01/01/2020 15:30:30 job_1 禁用
01/01/2020 18:30:30 job_1 失败
01/01/2020 08:30:30 job_2 成功
01/01/2020 18:30:30 job_2 禁用
01/02/2020 15:30:30 job_1 成功
01/02/2020 08:30:30 job_2 成功
01/02/2020 18:30:30 job_2 成功

我正在尝试按日期(注意,而不是日期/时间)和job_name对这些数据进行分组。每个日期(注意,不是日期/时间)我想返回作业是否成功。

如果某一天作业的一次运行(或唯一一次运行)为“失败”,则无论当天的其他“状态”结果如何,我的分组行都必须返回“失败”。

如果某一天作业的一次运行(或唯一一次运行)为“已禁用”,并且当天该作业没有“失败”运行,则分组行必须返回“已禁用”。

如果一天内作业的所有运行都是“成功”,则分组行必须返回“成功”。

因此,我的示例表的预期结果为:

date_time job_name 地位
01/01/2020 job_1 失败
01/01/2020 job_2 禁用
01/02/2020 job_1 成功
01/02/2020 job_2 成功

我尝试在 date_time 和 job_name 列上使用 GROUP BY 语句来获得此结果,但我无法实现何时返回“成功”、“失败”和“禁用”状态的逻辑。

任何帮助都是值得赞赏的!

SQL 日期 分组依据

评论


答:

2赞 Tim Biegeleisen 11/6/2023 #1

使用条件聚合,我们可以尝试:

SELECT
    DATE(date_time) AS date_time,
    job_name,
    CASE WHEN COUNT(CASE WHEN status = 'failed' THEN 1 END) > 0
         THEN 'failed'
         WHEN COUNT(CASE WHEN status = 'disabled' THEN 1 END) > 0
         THEN 'disabled'
         WHEN COUNT(CASE WHEN status != 'success' THEN 1 END) = 0
         THEN 'success' END AS status
FROM yourTable
GROUP BY
    DATE(date_time),
    job_name
ORDER BY
    DATE(date_time),
    job_name;

评论

0赞 Ann L. 11/7/2023
这些不应该是陈述吗?即使 case 语句产生 NULL,您也不会获得 COUNT() 的值吗?SUM(CASE WHEN ... )
0赞 Ann L. 11/7/2023
但如果你没有信心,你可能不会这样写,而且你确实有 505K 分。:)
1赞 Tim Biegeleisen 11/7/2023
@AnnL。我更喜欢在这里使用,而不是因为前者避免了表达。COUNT()SUM()ELSE
0赞 basrood 11/7/2023
谢谢,这个和 Xmehdi 的回答奏效了!
0赞 XM01 - stands with Palestine 11/7/2023 #2

结合使用 with 语句、聚合函数和:GROUP BYCASECOUNTSUM

SELECT 
  CONVERT(DATE, date_time) AS date, --to extract only the date portion from the date_time column.
  job_name,
  CASE
    WHEN COUNT(*) = SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) THEN 'failed'
    WHEN SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) > 0 THEN 'failed'
    WHEN SUM(CASE WHEN status = 'disabled' THEN 1 ELSE 0 END) > 0 THEN 'disabled'
    ELSE 'success'
  END AS status
FROM your_table
GROUP BY CONVERT(DATE, date_time), job_name;

dbfiddle 演示

这是聚合函数的替代方案:MAX

SELECT 
  CONVERT(DATE, date_time) AS date,
  job_name,
  CASE
    WHEN MAX(CASE WHEN status = 'failed' THEN 2 WHEN status = 'disabled' THEN 1 ELSE 0 END) = 2 THEN 'failed'
    WHEN MAX(CASE WHEN status = 'disabled' THEN 1 ELSE 0 END) = 1 THEN 'disabled'
    ELSE 'success'
  END AS status
FROM your_table
GROUP BY CONVERT(DATE, date_time), job_name;

DBFiddle MAX 演示

评论

1赞 Ann L. 11/7/2023
这与Tim Biegeleisen的回答有何不同?
1赞 Tim Biegeleisen 11/7/2023
此答案不会明确检查所有记录是否都匹配。这可能是一个可行的假设,也可能不是一个可行的假设。success
0赞 XM01 - stands with Palestine 11/7/2023
实际上,我也使用了聚合函数,请查看:MAX
0赞 Ann L. 11/7/2023 #3

为了完整起见,这里有另一个解决方案,一个使用 PIVOT 的解决方案。我无法预测哪种解决方案可能表现得更好:你必须进行实验。

SELECT [date]
    ,  job_name
    ,  CASE 
          WHEN  [failed] > 0
          THEN  'failed'   
          WHEN  [disabled] > 0 and [failed] = 0
          THEN  'disabled'
          WHEN  [disabled] = 0 and [failed] = 0 and [success] > 0
          THEN  'success'
      END as status 
  FROM  (
      SELECT 
         CONVERT(DATE, date_time) as [date]
      ,  job_name
      ,  status
      from your_table
      ) as v
  PIVOT (COUNT(status) 
  FOR status IN (success, failed, disabled)) as c
ORDER BY 
       [date]
     , job_name

DBFiddle(基于 XMedhi01 设置的示例)