在Redshift/Postgres中,如何计算符合条件的行?

ILi*_*cos 43 postgresql amazon-redshift

我正在尝试编写一个只计算符合条件的行的查询.

例如,在MySQL中我会这样写:

SELECT
    COUNT(IF(grade < 70), 1, NULL)
FROM
    grades
ORDER BY
    id DESC;
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试在Redshift上执行此操作时,它会返回以下错误:

错误:函数if(布尔,整数,"未知")不存在

提示:没有函数匹配给定的名称和参数类型.您可能需要添加显式类型转换.

我检查了条件语句的文档,我找到了

NULLIF(value1, value2)

但它只比较value1和value2,如果这些值相等,则返回null.

我找不到一个简单的IF语句,乍一看我找不到办法去做我想做的事情.

我试图使用CASE表达式,但我没有得到我想要的结果:

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades
Run Code Online (Sandbox Code Playgroud)

这是我想要计算的方式:

  • 失败(等级<70)

  • 平均值(70 <=等级<80)

  • 好(80 <=等级<90)

  • 优秀(90 <=等级<= 100)

这就是我期望看到的结果:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|   4    |    2    |  1   |     4     |
+========+=========+======+===========+
Run Code Online (Sandbox Code Playgroud)

但是我得到了这个:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|  11    |   11    |  11  |    11     |
+========+=========+======+===========+
Run Code Online (Sandbox Code Playgroud)

我希望有人能指出我正确的方向!

如果这有助于这里的一些示例信息

CREATE TABLE grades(
  grade integer DEFAULT 0,
);

INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94);
Run Code Online (Sandbox Code Playgroud)

yie*_*ood 131

首先,你在这里遇到的问题是你说的是"如果等级小于70,这个案例表达式的值是count(rank).否则,这个表达式的值是count(rank) ".所以,在任何一种情况下,你总是得到相同的价值.

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这样,case表达式仅在测试表达式为true时计算为1,否则为null.然后count()将只计算非null实例,即当测试表达式为true时,它应该为您提供所需的内容.

编辑:作为旁注,请注意这与您最初编写此文件的方式完全相同count(if(test, true-value, false-value)),仅重写为count(case when test then true-value end)(并且由于else未提供给案例,因此null为false值).

编辑: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
Run Code Online (Sandbox Code Playgroud)

  • Case是官方标准;)if()函数是MySQL,它是SQL Server中的iif(). (8认同)
  • [汇总表达式在此处记录.](https://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES) (3认同)

小智 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
Run Code Online (Sandbox Code Playgroud)

如果您想按分类列对计数进行分组,则可以正常工作.


mys*_*yst 5

@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
Run Code Online (Sandbox Code Playgroud)

但是既然你谈到了NULLIF(value1, value2),那么有一种 nullif 方法可以给出相同的结果:

select count(nullif(grade < 70 ,true)) as failed from grades;


Tau*_*kas 5

仅红移

对于懒惰的打字者,这里有一个COUNTIF基于@user1509107答案构建的“”总和整数转换版本:

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
Run Code Online (Sandbox Code Playgroud)