GROUP BY + CASE语句

ssb*_*sts 25 sql postgresql group-by case aggregate-functions

我有一个工作查询,按硬件模型和结果对数据进行分组,但问题是有很多"结果".我试图将其减少到"如果结果= 0然后保持为0,否则将其设置为1".这通常有效,但我最终得到:

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    42
 2013-11-06 | modelA         |    1 |    1 |     2
 2013-11-06 | modelA         |    1 |    1 |    11
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    16
 2013-11-06 | modelB         |    1 |    1 |     8
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |     5
 2013-11-06 | modelB         |    3 |    1 |     7
 2013-11-06 | modelB         |    3 |    1 |   563
Run Code Online (Sandbox Code Playgroud)

而不是我试图实现的聚合,每种类型/案例组合只有1行.

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    55
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    24
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |   575
Run Code Online (Sandbox Code Playgroud)

这是我的查询:

select CURRENT_DATE-1 AS day, model.name, attempt.type, 
       CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END, 
       count(*) 
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by model.name, attempt.type, attempt.result
order by model.name, attempt.type, attempt.result;
Run Code Online (Sandbox Code Playgroud)

关于如何实现这一目标的任何提示都会很棒.

日期将始终在WHERE条款中定义,因此不会有所不同.name, type, result(case)count会有所不同.简而言之,对于任何给定的模型,我每个"type + case"组合只需要1行.正如你在第一个结果集看到我有3个排的modelAtype=1case=1(因为有很多的"结果",我已经变成值0 = 0和其他任何= 1).我希望将其表示为1行,其中聚合计数如示例数据集2中所示.

Erw*_*ter 60

您的查询已经有效 - 除了您遇到命名冲突或只是将输出列(CASE表达式)与源列 混淆result,后者具有不同的内容.

...
GROUP BY model.name, attempt.type, attempt.result
...
Run Code Online (Sandbox Code Playgroud)

你需要GROUP BY你的CASE表达,而不是你的源列:

...
GROUP BY model.name, attempt.type
       , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...
Run Code Online (Sandbox Code Playgroud)

或者提供与列表中的任何列名称不同的列别名FROM - 否则该列优先:

SELECT ...
     , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...
Run Code Online (Sandbox Code Playgroud)

SQL标准在这方面非常特殊.在这里引用手册:

输出列的名称可用于引用列的值in ORDER BYGROUP BY子句,但不能用于WHEREHAVING子句; 那里你必须写出表达式.

和:

如果ORDER BY表达式是与输出列名称和输入列名称都匹配的简单名称,ORDER BY则会将其解释为输出列名称.这与GROUP BY在相同情况下 做出的选择相反.这种不一致性与SQL标准兼容.

大胆强调我的.

通过使用位置引用(序号)GROUP BY和从左到右ORDER BY引用SELECT列表中的项目可以避免这些冲突.见下面的解决方案
缺点是,这可能更难以阅读并且容易受到SELECT列表中的编辑的影响(人们可能忘记相应地调整位置引用).

但是,你必须将列添加dayGROUP BY子句,只要它保持一个恒定值(CURRENT_DATE-1).

使用正确的JOIN语法和位置引用重写和简化它可能如下所示:

SELECT m.name
     , a.type
     , CASE WHEN a.result = 0 THEN 0 ELSE 1 END AS result
     , CURRENT_DATE - 1 AS day
     , count(*) AS ct
FROM   attempt    a
JOIN   prod_hw_id p USING (hard_id)
JOIN   model      m USING (model_id)
WHERE  ts >= '2013-11-06 00:00:00'  
AND    ts <  '2013-11-07 00:00:00'
GROUP  BY 1,2,3
ORDER  BY 1,2,3;
Run Code Online (Sandbox Code Playgroud)

另请注意,我正在避免使用列名time.这是一个保留字,不应该用作标识符.此外,你的"时间"显然是一个timestamp或者date,所以这是相当误导的.


小智 7

请你试试这个:用下面的一个代替case case

Sum(CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END) as Count,
Run Code Online (Sandbox Code Playgroud)


小智 6

仅当别名在上一步中引入时才可以使用。因此SELECT子句中的别名可以在 the 中使用,ORDER BY但不能在 theGROUP BY子句中使用。

参考:Microsoft T-SQL 文档以供进一步阅读。

FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。