Postgresql

WHERE 子句不阻止返回行但更改聚合函式的值(此處:計數)

  • February 13, 2020

我最近發現了一種 PostgreSQL 行為,在我看來這是一種奇怪且有問題的行為。

考慮這個簡單的查詢

SELECT 
  'something confidential'
WHERE FALSE;

無意義的子句WHERE FALSE在這裡類似於一個非常嚴格的權限檢查。這按預期工作:沒有返回任何內容。

現在,假設您添加了一個附加列,例如一個count函式。這給出了這個查詢:

SELECT 
  'something confidential'
  ,count(CURRENT_DATE)
WHERE FALSE;

(的參數count可以是任何東西。CURRENT_DATE只是一個隨機的例子。)

現在,我們得到

機密的東西| 0

我想,這是故意的,SELECT count(CURRENT_DATE);如果條件為真則返回 1,如果條件為假則返回 0。

但是,我認為在您不知道的情況下這是有問題的。所以,我的問題是

  1. 怎麼會?這種行為的背景是什麼?
  2. 如何使這樣的查詢返回零行而不是值為 0 的一行count
  3. 有沒有辦法直接在查詢中通過添加聚合函式來防止意外返回您不想返回的行?(即除了外部測試等)

我使用的是 PostgreSQL 12.1,但舊版本的行為是相同的。

這是幾乎所有數據庫的標準 SQL 行為。

返回行的方式有以下三種:

  1. 沒有聚合函式,查詢每行返回一行;
  2. 使用聚合函式,但沒有分組,查詢只返回一行;
  3. 使用聚合函式和分組,查詢為每個組返回一行(即,為分組列中的每個唯一值組合)。

換句話說,沒有分組的聚合表現得好像只有一個組,即,就好像你已經寫GROUP BY ()了(帶有空列表的 GROUP BY 子句會導致單個組“無處”,類似於沒有 FROM 的查詢結果)在單行’從無處’)。

為防止返回此單組行,您可以

  • 添加一個顯式的 GROUP BY,以便您在上面的情況 3 中結束(在 WHERE 過濾掉所有行之後,沒有可以從中創建組的值):
SELECT 
  'something confidential',
  count(CURRENT_DATE)
WHERE FALSE
GROUP BY 1;  -- refers to the first column
  • 將聚合移動到子查詢中,以便權限檢查影響其輸出:
SELECT *
FROM (SELECT
         'something confidential',
         count(CURRENT_DATE)
    ) AS subquery
WHERE FALSE;
  • 將權限檢查移到 HAVING 子句中(如果可能的話),以便丟棄單個組的輸出:
SELECT 
  'something confidential',
  count(CURRENT_DATE)
HAVING FALSE;

引用自:https://dba.stackexchange.com/questions/259604