sql - 具有 SUM 聚合的 Postgres CASE 条件评估不需要的 ELSE 部分

标签 sql postgresql

根据 Postgres documentation :

A CASE expression does not evaluate any subexpressions that are not needed to determine the result. For example, this is a possible way of avoiding a division-by-zero failure:

SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END;

为什么以下表达式返回 ERROR: division by zero? - 显然是在评估 else 部分:

SELECT CASE WHEN SUM(0) = 0 THEN 42 ELSE 43 / 0 END

同时

SELECT CASE WHEN SUM(0) = 0 THEN 42 ELSE 43 END

返回 42。

编辑:所以上面的例子失败了,因为 Postgres 已经在计划阶段计算了不可变值 (43/0)。我们的实际查询看起来更像这样:

case when sum( column1 ) = 0
            then 0
            else round( sum(   price 
                             * hours 
                             / column1 ), 2 )

虽然这个查询看起来不是一成不变的(取决于实际值),但仍然存在被零除的错误。当然 sum(column1) 在我们的例子中实际上是 0。

最佳答案

有趣的例子。这确实有一个很好的解释。假设您有这样的数据:

db=# table test;
 column1 | price | hours 
---------+-------+-------
       1 |     2 |     3
       3 |     2 |     1

PostgreSQL 分两次执行您的 SELECT,首先它会计算所有存在的聚合函数(如 sum()):

db=# select sum(column1) as sum1, sum(price * hours / column1) as sum2 from test;
 sum1 | sum2 
------+------
    4 |    6

然后它会将这些结果插入您的最终表达式并计算实际结果:

db=# with temp as (
db(#     select sum(column1) as sum1, sum(price * hours / column1) as sum2 from test
db(# ) select case when sum1 = 0 then 0 else round(sum2, 2) end from temp;
 round 
-------
  6.00

现在很明显,如果第一次聚合过程中出现错误,它永远不会到达 CASE 语句。

因此,这在有关 CASE 语句的文档中并不是真正的问题——它适用于所有条件构造——而是关于在 SELECT 语句中处理聚合的方式。这种问题不会在任何其他上下文中发生,因为聚合只允许在 SELECT 中使用。

但在这种情况下,文档也确实需要更新。本例中的正确文档是“the general processing of SELECT”。第 4 步讨论了 GROUP BY 和 HAVING 子句,但它实际上也评估了此步骤中的任何聚合函数,而不管 GROUP BY/HAVING。并且您的 CASE 语句在步骤 #5 中被评估。

解决方案

常见的解决方案是,如果您想忽略否则会导致被零除的聚合输入,请使用 nullif() construct将它们变成 NULL:

round( sum(   price 
            * hours 
            / nullif(column1, 0) ), 2 )

PostgreSQL 9.4 将为聚合引入一个新的 FILTER 子句,它也可以用于此目的:

round( sum(   price 
            * hours 
            / column1
          ) filter (where column1!=0), 2 )

关于sql - 具有 SUM 聚合的 Postgres CASE 条件评估不需要的 ELSE 部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26297035/

相关文章:

sql - Dynamics CRM 2011 安全角色 SQL 查询

postgresql - Flyway - Flyway 架构迁移失败

django - 关系不存在,在 PostgreSQL、Django 中

mysql - 外键的语法差异 MySQL/PostgresQL

sql - 在 SQL 连接方面需要帮助

SQL 到 JSON - 将结果分组到 JSON 数组中

.net - 如何捕获带有替换参数的 SQL? (.NET,SqlCommand)

mysql - 如何在sql中使用join连接两个表?

java - 在 postgresql 中存储图像

python - 在 Django 框架中使用动态模型