簡體   English   中英

帶SUM聚合的Posgtres CASE條件評估不需要ELSE部分

[英]Posgtres CASE condition with SUM aggregation evaluates not needed ELSE part

根據Postgres文件

CASE表達式不會評估確定結果所不需要的任何子表達式。 例如,這是避免零除失敗的可能方法:

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

為什么以下表達式返回ERROR: division by zero? - 顯然評估了其他部分:

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中允許。

但在這種情況下,文檔確實需要更新。 本例中的正確文檔是“ SELECT的一般處理 ”。 步驟#4討論了GROUP BY和HAVING子句,但它實際上也評估了此步驟中的任何聚合函數,無論GROUP BY / HAVING如何。 並且您的CASE語句將在步驟#5中進行評估。

常見的解決方案是,如果要忽略否則會導致除零的聚合輸入,請使用nullif()構造將它們轉換為NULL:

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

PostgreSQL 9.4將為聚合引入一個新的FILTER子句,它也可用於此目的:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM