我正在尝试编写一个只计算符合条件的行的查询。
例如,在MySQL中我会这样写:
SELECT
COUNT(IF(grade < 70), 1, NULL)
FROM
grades
ORDER BY
id DESC;
但是,当我尝试在Redshift上执行此操作时,它会返回以下错误:
错误:函数if(布尔,整数,“未知”)不存在
提示:没有函数匹配给定的名称和参数类型。您可能需要添加显式类型转换。
我检查了条件语句的文档,我找到了
NULLIF(value1, value2)
但它只比较value1和value2,如果这些值相等,则返回null。
我找不到一个简单的IF语句,乍一看我找不到办法去做我想做的事。
我尝试使用CASE表达式,但是我没有得到我想要的结果:
SELECT
CASE
WHEN grade < 70 THEN COUNT(rank)
ELSE COUNT(rank)
END
FROM
grades
这是我想要计算的方式:
失败(等级<70)
平均值(70 <=等级<80)
好(80 <=等级<90)
优秀(90 <=等级<= 100)
这就是我期望看到结果的方式:
+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
| 4 | 2 | 1 | 4 |
+========+=========+======+===========+
但我得到了这个:
+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
| 11 | 11 | 11 | 11 |
+========+=========+======+===========+
我希望有人能指出我正确的方向!
如果这有助于这里的一些示例信息
CREATE TABLE grades(
grade integer DEFAULT 0,
);
INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94);
答案 0 :(得分:122)
首先,你在这里遇到的问题是你说的是“如果等级小于70,这个案例表达式的值是count(rank)。否则,这个表达式的值是count (秩)。”所以,在任何一种情况下,你总是得到相同的价值。
SELECT
CASE
WHEN grade < 70 THEN COUNT(rank)
ELSE COUNT(rank)
END
FROM
grades
count()只计算非空值,所以通常你会看到完成你正在尝试的模式是:
SELECT
count(CASE WHEN grade < 70 THEN 1 END) as grade_less_than_70,
count(CASE WHEN grade >= 70 and grade < 80 THEN 1 END) as grade_between_70_and_80
FROM
grades
这样,case表达式仅在测试表达式为true时计算为1,否则为null。然后count()将只计算非null实例,即当测试表达式为真时,它应该为您提供所需的。
编辑:作为旁注,请注意这与您最初使用count(if(test, true-value, false-value))
编写此内容的方式完全相同,仅重写为count(case when test then true-value end)
(并且null是错误的 - 由于未向案例提供else
,因此值。
编辑:postgres 9.4在原始交换后几个月发布。该版本引入了聚合过滤器,可以使这样的场景看起来更好更清晰。这个答案仍然偶尔会有一些赞成,所以如果你偶然发现并且正在使用更新的postgres(即9.4+),你可能会想要考虑这个等效的版本:
SELECT
count(*) filter (where grade < 70) as grade_less_than_70,
count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
grades
答案 1 :(得分:11)
另一种方法:
SELECT
sum(CASE WHEN grade < 70 THEN 1 else 0 END) as grade_less_than_70,
sum(CASE WHEN grade >= 70 and grade < 80 THEN 1 else 0 END) as grade_between_70_and_80
FROM
grades
如果您想按分类列对计数进行分组,则工作正常。
答案 2 :(得分:2)
@yieldsfalsehood提供的解决方案可以完美地工作:
SELECT
count(*) filter (where grade < 70) as grade_less_than_70,
count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
grades
但是自从您谈论NULLIF(value1, value2)
以来,nullif有一种方法可以提供相同的结果:
select count(nullif(grade < 70 ,true)) as failed from grades;
答案 3 :(得分:0)
仅Redshift
对于懒惰的打字员,这是一个构建在@ user1509107答案之上的“ COUNTIF
”和整数转换版本:
SELECT
SUM((grade < 70)::INT) AS grade_less_than_70,
SUM((grade >= 70 AND grade < 80)::INT) AS grade_between_70_and_80
FROM
grades