条件 SQL 计数

Conditional SQL count

创建用于计算 table 中数据出现次数的列的最佳方法是什么? table需要按一列分组吗?
我的数据库是 PostgreSQL。

我看过:

SELECT
    sum(CASE WHEN question1 = 0 THEN 1 ELSE 0 END) AS ZERO,
    sum(CASE WHEN question1 = 1 THEN 1 ELSE 0 END) AS ONE,
    sum(CASE WHEN question1 = 2 THEN 1 ELSE 0 END) AS TWO,
    category
FROM reviews
    GROUP BY category

其中 question1 的值可以是 012.

我还看到了一个使用 count(CASE WHEN question1 = 0 THEN 1)

的版本

但是,随着 question1 的可能值数量的增加,编写起来会变得更加麻烦。有没有方便的方法来编写这个查询,可能优化性能?

"best" 方法(对我来说)是编写如下查询:

SELECT
    category,
    question1,
    count(*)
FROM reviews
GROUP BY category, question1

然后我用这个数据在应用逻辑中画了一个table

另一种选择是对所有分组结果使用一个 JSON 列。这将导致类似:

category1 | {"zero": 1, "one": 3, "two": 5}
category2 | {"one": 7, "two": 4}

等等。

您可以使用 json_build_objectjson_agg 从上一个选项构建对此选项的查询。这个选项的最好的事情 - 你不需要提前知道可能的 question1 值的数量。

在 Postgres 9.4 或更高版本中,使用聚合 FILTER 选项。通常最干净和最快:

SELECT category
     , count(*) FILTER (WHERE question1 = 0) AS zero
     , count(*) FILTER (WHERE question1 = 1) AS one
     , count(*) FILTER (WHERE question1 = 2) AS two
FROM   reviews
GROUP  BY 1;

FILTER 子句的详细信息:

  • Aggregate columns with additional (distinct) filters

如果你想要:

SELECT category
     , count(question1 = 0 OR NULL) AS zero
     , count(question1 = 1 OR NULL) AS one
     , count(question1 = 2 OR NULL) AS two
FROM   reviews
GROUP  BY 1;

更多语法变体:

正确的交叉表查询

crosstab() 产生最佳性能并且对于长选项列表更短:

SELECT * FROM crosstab(
     'SELECT category, question1, count(*) AS ct
      FROM   reviews
      GROUP  BY 1, 2
      ORDER  BY 1, 2'
   , 'VALUES (0), (1), (2)'
   ) AS ct (category text, zero int, one int, two int);

详细解释:

  • PostgreSQL Crosstab Query