Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/87.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 带总和聚合的Posgtres案例条件求值不需要其他部分_Sql_Postgresql - Fatal编程技术网

Sql 带总和聚合的Posgtres案例条件求值不需要其他部分

Sql 带总和聚合的Posgtres案例条件求值不需要其他部分,sql,postgresql,Sql,Postgresql,根据: CASE表达式不会计算任何不正确的子表达式 需要确定结果。例如,这是一种可能的方法 避免零除故障: 选择。。。当x 0时,则y/x>1.5,否则为假端 为什么下面的表达式返回一个错误:被零除?-显然是在计算else部分: SELECT CASE WHEN SUM(0) = 0 THEN 42 ELSE 43 / 0 END 当 返回42 编辑:所以上面的例子失败了,因为Postgres计算的是已经在计划阶段的不可变值(43/0)。我们的实际查询更像这样: case when sum(

根据:

CASE表达式不会计算任何不正确的子表达式 需要确定结果。例如,这是一种可能的方法 避免零除故障:

选择。。。当x 0时,则y/x>1.5,否则为假端

为什么下面的表达式返回一个
错误:被零除?
-显然是在计算else部分:

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

返回42

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

case when sum( column1 ) = 0
            then 0
            else round( sum(   price 
                             * hours 
                             / column1 ), 2 )
db=# table test;
 column1 | price | hours 
---------+-------+-------
       1 |     2 |     3
       3 |     2 |     1

尽管此查询看起来不是不可变的(取决于实际值),但仍然存在被零除的错误。当然,在我们的例子中,sum(第1列)实际上是0。

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

case when sum( column1 ) = 0
            then 0
            else round( sum(   price 
                             * hours 
                             / column1 ), 2 )
db=# table test;
 column1 | price | hours 
---------+-------+-------
       1 |     2 |     3
       3 |     2 |     1
PostgreSQL分两次执行SELECT,首先它将计算所有聚合函数(如
sum()
):

然后将这些结果插入到最终表达式中,并计算实际结果:

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中才允许聚合

但在这种情况下,文档也需要更新。此实例中的正确文档是“”。步骤4讨论了GROUP BY和HAVING子句,但实际上它也会在这一步中计算任何聚合函数,而不管GROUP BY/HAVING如何。您的案例陈述将在步骤5中进行评估

解决方案 常见的解决方案是,如果要忽略否则会导致除零的聚合输入,请使用将它们转换为null:

round( sum(   price 
            * hours 
            / nullif(column1, 0) ), 2 )
PostgreSQL 9.4将为聚合引入一个新的筛选子句,该子句也可用于此目的:

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

您的报价后面紧接着是“注意:如第35.6节所述,标记为不可变的函数和运算符可以在计划查询时而不是在执行查询时进行求值。这意味着在查询执行期间未求值的子表达式的常量部分仍可以在查询计划期间进行求值。”那么到底是什么问题呢?有意义的检查类型如前所述工作,只有没有意义的检查(常量
43/0
value?)才会抛出错误。谢谢,我以前没有读过说明。我现在明白为什么我问题中的简化示例失败了。让我添加一个更接近真实代码的示例-我不确定文档中的注释是否也解释了这一点。实际上,PostgreSQL跨越了一系列障碍,以使其正常工作。例如,
选择CASE WHEN TRUE,然后选择1 ELSE 1/0 END
成功。您已经发现了一种情况,PostgreSQL通常试图延迟零除法求值,但从外观上看,这可能与聚合函数有关。我不确定这是否是一个严格意义上的bug(这将取决于SQL标准的详细信息),但请将其报告给pgsql bug。贴一个链接到这篇文章,但也有一个完全详细的解释,就像你上面提供的一样。我作为一个错误报告提交了这篇文章,并将在我们了解更多信息后尽快更新这个问题。这是邮件列表存档的链接:好吧,在你对这个问题引起更多注意后,重新阅读我的答案,我意识到我甚至没有找到正确的解决方法。我的方法是可行的,但实际上你已经把你的方法变成了可行的方法。谢谢你的有益解释和更好的解决方案。