postgresql GROUP BY + CASE语句

vvppvyoh  于 2022-11-23  发布在  PostgreSQL
关注(0)|答案(5)|浏览(213)

我有一个按硬件型号和结果对数据进行分组的工作查询,但问题是有许多 “结果”。我曾尝试将其简化为 “如果结果= 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

而不是我试图实现的聚合,其中每个类型/案例组合只有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

下面是我的疑问:

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;

任何关于我如何实现这一点的提示都会很棒。
Day将始终在WHERE子句中定义,因此它不会变化。name, type, result(case)count将变化。简而言之,对于任何给定的模型,每个 “type + case” 组合只需要1行。(因为有许多 “result” 值,我已经将其转换为 0=0,其他任何值=1)。我希望将其表示为1行,并将计数聚合为示例数据集2中的计数。

zzwlnbp8

zzwlnbp81#

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

...
GROUP BY model.name, attempt.type, attempt.result
...

您需要GROUP BY您的CASE运算式,而不是您的来源数据行:

...
GROUP BY model.name, attempt.type
       , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...

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

SELECT ...
     , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...

SQL标准在这方面是相当特殊的。
输出列的名称可用于引用ORDER BYGROUP BY子句中的列值,但不能用于引用WHEREHAVING子句中的列值。在这里必须写出表达式。
还有:
如果ORDER BY表达式是一个与输出列名和输入列名都匹配的简单名称,则ORDER BY会将其解释为输出列名。这与GROUP BY在相同情况下所做的选择相反。这种不一致性是为了与SQL标准兼容。

粗体强调是我

通过在GROUP BYORDER BY中使用位置引用(序号),从左到右引用SELECT列表中的项,可以避免这些冲突。请参阅下面的解决方案。
缺点是这可能更难阅读,并且容易在SELECT列表中编辑:人们可能忘记相应地调整位置基准。
但是,只要列day包含常量值(CURRENT_DATE-1),就 * 不必 * 将该列添加到GROUP BY子句中。
使用正确的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;

我避免使用列名time。这是一个reserved word,不应该用作标识符。此外,您的“time”显然是一个timestamp or date,所以这是相当误导的。

qyswt5oh

qyswt5oh2#

你能试试这个吗:将case语句替换为以下语句

Sum(CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END) as Count,
o3imoua4

o3imoua43#

只有在上一步中引入了别名,才能使用别名。因此,SELECT子句中的别名可以在ORDER BY中使用,但不能在GROUP BY子句中使用。
指涉:Microsoft T-SQL文件以供进一步阅读。

FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP

希望这对你有帮助。

gxwragnw

gxwragnw4#

尝试将其他两个非COUNT列添加到GROUP BY:

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 1,2,3,4
order by model.name, attempt.type, attempt.result;
9fkzdhlc

9fkzdhlc5#

对于TSQL,我喜欢将case语句封装在一个外部apply中,这样我就不必编写两次case语句,允许在将来的连接中通过别名引用case语句,并避免了对位置引用的需要。

select oa.day, 
model.name, 
attempt.type, 
oa.result
COUNT(*) MyCount 
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
OUTER APPLY (
    SELECT CURRENT_DATE-1 AS day,
     CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END result
    ) oa    
group by oa.day, 
model.name, 
attempt.type, 
oa.result
order by model.name, attempt.type, oa.result;

相关问题