一個 表表達式 計算出一個表。表表達式包含一個 FROM
子句,後面可以選擇性地跟著 WHERE
, GROUP BY
, 和 HAVING
子句。簡單的表表達式只是引用磁碟上的一個表,稱為基表,但更復雜的表示式可用於以各種方式修改或組合基表。
表表達式中的可選 WHERE
, GROUP BY
, 和 HAVING
子句指定了一個處理 FROM
子句中派生的表的連續轉換管道。所有這些轉換都會生成一個虛擬表,該表提供傳遞給 select 列表以計算查詢的輸出行的行。
FROM
子句 #FROM
子句從逗號分隔的表引用列表中給定的一到多個其他表派生出一個表。
FROMtable_reference
[,table_reference
[, ...]]
表引用可以是表名(可能帶有模式限定),也可以是派生表,例如子查詢、JOIN
構造,或它們的複雜組合。如果 FROM
子句中列出了多個表引用,則這些表將進行交叉連線(即,形成其行的笛卡爾積;見下文)。FROM
列表的結果是一箇中間虛擬表,然後該表可以根據 WHERE
, GROUP BY
, 和 HAVING
子句進行轉換,並最終成為整個表表達式的結果。
當表引用命名的表是表繼承層次結構的父表時,該表引用將產生該表及其所有子表的行,除非關鍵字 ONLY
出現在表名前面。但是,該引用只產生命名錶中出現的列——子表中新增的任何列都將被忽略。
除了在表名前面寫 ONLY
之外,還可以在表名後面寫 *
來顯式指定包含子表。現在沒有實際理由再使用這種語法了,因為搜尋子表現在總是預設行為。但是,為了與舊版本相容,它仍然受支援。
連線表是根據特定連線型別的規則從其他兩個(真實或派生的)表派生的表。提供內部連線、外部連線和交叉連線。連線表的通用語法是
T1
join_type
T2
[join_condition
]
所有型別的連線都可以串聯或巢狀:T1
和 T2
都可以是連線表。括號可用於 JOIN
子句周圍以控制連線順序。在沒有括號的情況下,JOIN
子句從左到右巢狀。
連線型別
T1
CROSS JOINT2
對於 T1
和 T2
的每一可能行組合(即,行的笛卡爾積),連線表將包含一行,該行由 T1
中的所有列後跟 T2
中的所有列組成。如果表分別有 N 和 M 行,則連線表將有 N * M 行。
FROM
等同於 T1
CROSS JOIN T2
FROM
(見下文)。它也等同於 T1
INNER JOIN T2
ON TRUEFROM
。T1
, T2
當出現多於兩個表時,後一種等價關係不完全成立,因為 JOIN
的結合性比逗號更強。例如 FROM
與 T1
CROSS JOIN T2
INNER JOIN T3
ON condition
FROM
不同,因為在第一種情況下 T1
, T2
INNER JOIN T3
ON condition
condition
可以引用 T1
,而在第二種情況下則不能。
T1
{ [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOINT2
ONboolean_expression
T1
{ [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOINT2
USING (join column list
)T1
NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOINT2
在所有形式中,INNER
和 OUTER
這兩個詞是可選的。INNER
是預設值;LEFT
, RIGHT
, 和 FULL
暗示外部連線。
連線條件在 ON
或 USING
子句中指定,或者透過關鍵字 NATURAL
隱式指定。連線條件決定兩個源表中的哪些行被認為是 “匹配”,如下文詳細解釋。
限定連線的可能型別是
內部連線
對於 T1 的每一行 R1,連線表都有一個行,該行對應於滿足 R1 的連線條件的 T2 中的每一行。
LEFT OUTER JOIN
首先,執行內部連線。然後,對於 T1 中與 T2 中的任何行都不滿足連線條件的每一行,都會新增一個連線行,其中 T2 的列值為 null。因此,連線表總是至少有一個行對應於 T1 中的每一行。
RIGHT OUTER JOIN
首先,執行內部連線。然後,對於 T2 中與 T1 中的任何行都不滿足連線條件的每一行,都會新增一個連線行,其中 T1 的列值為 null。這是左連線的相反操作:結果表將始終有一個行對應於 T2 中的每一行。
FULL OUTER JOIN
首先,執行內部連線。然後,對於 T1 中與 T2 中的任何行都不滿足連線條件的每一行,都會新增一個連線行,其中 T2 的列值為 null。此外,對於 T2 中與 T1 中的任何行都不滿足連線條件的每一行,都會新增一個連線行,其中 T1 的列值為 null。
ON
子句是最通用的連線條件:它接受一個布林值表示式,該表示式與 WHERE
子句中使用的表示式是同一種類型。來自 T1
和 T2
的行對匹配,如果 ON
表示式求值為 true。
USING
子句是一種簡寫,它允許您利用連線雙方使用相同名稱的連線列(或列)的特定情況。它接受一個共享列名的逗號分隔列表,並形成一個包含每個列的等值比較的連線條件。例如,使用 USING (a, b)
連線 T1
和 T2
會生成連線條件 ON
。T1
.a = T2
.a AND T1
.b = T2
.b
此外,JOIN USING
的輸出會抑制冗餘列:沒有必要列印匹配列中的兩列,因為它們的值必須相等。JOIN ON
會產生 T1
的所有列,然後是 T2
的所有列,而 JOIN USING
會為每個列對(按列表順序)產生一個輸出列,然後是 T1
中的任何剩餘列,然後是 T2
中的任何剩餘列。
最後,NATURAL
是 USING
的一種簡寫形式:它形成一個 USING
列表,該列表包含出現在兩個輸入表中的所有列名。與 USING
一樣,這些列在輸出表中只出現一次。如果沒有共同的列名,NATURAL JOIN
的行為就像 CROSS JOIN
。
USING
對於連線關係中的列更改是相對安全的,因為只有列出的列會被合併。NATURAL
的風險要大得多,因為任何對任一關係的模式更改,導致出現新的匹配列名,都會導致連線也合併該新列。
總而言之,假設我們有表 t1
num | name -----+------ 1 | a 2 | b 3 | c
和 t2
num | value -----+------- 1 | xxx 3 | yyy 5 | zzz
然後,對於各種連線,我們得到以下結果
=>
SELECT * FROM t1 CROSS JOIN t2;
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 1 | a | 3 | yyy 1 | a | 5 | zzz 2 | b | 1 | xxx 2 | b | 3 | yyy 2 | b | 5 | zzz 3 | c | 1 | xxx 3 | c | 3 | yyy 3 | c | 5 | zzz (9 rows)=>
SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 3 | c | 3 | yyy (2 rows)=>
SELECT * FROM t1 INNER JOIN t2 USING (num);
num | name | value -----+------+------- 1 | a | xxx 3 | c | yyy (2 rows)=>
SELECT * FROM t1 NATURAL INNER JOIN t2;
num | name | value -----+------+------- 1 | a | xxx 3 | c | yyy (2 rows)=>
SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | 3 | yyy (3 rows)=>
SELECT * FROM t1 LEFT JOIN t2 USING (num);
num | name | value -----+------+------- 1 | a | xxx 2 | b | 3 | c | yyy (3 rows)=>
SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 3 | c | 3 | yyy | | 5 | zzz (3 rows)=>
SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | 3 | yyy | | 5 | zzz (4 rows)
使用 ON
指定的連線條件也可以包含與連線不直接相關的條件。這對於某些查詢可能很有用,但需要仔細考慮。例如
=>
SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx 2 | b | | 3 | c | | (3 rows)
請注意,將限制放在 WHERE
子句中會產生不同的結果
=>
SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
num | name | num | value -----+------+-----+------- 1 | a | 1 | xxx (1 row)
這是因為放在 ON
子句中的限制在連線 之前 處理,而放在 WHERE
子句中的限制在連線 之後 處理。這對於內部連線無關緊要,但對於外部連線則非常重要。
可以為表和複雜的表引用指定一個臨時名稱,以便在查詢的其餘部分引用派生表。這稱為 表別名。
要建立表別名,請寫
FROMtable_reference
ASalias
或
FROMtable_reference
alias
AS
關鍵字是可選的。alias
可以是任何識別符號。
表別名的一個典型應用是為長表名分配短識別符號,以保持連線子句的可讀性。例如
SELECT * FROM some_very_long_table_name s JOIN another_fairly_long_name a ON s.id = a.num;
對於當前查詢而言,別名成為表引用的新名稱——在查詢的其餘部分不允許使用原始名稱引用該表。因此,以下是無效的
SELECT * FROM my_table AS m WHERE my_table.a > 5; -- wrong
表別名主要用於方便表示,但在將表自身連線起來時是必需的,例如
SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;
括號用於解決歧義。在下面的示例中,第一個語句將別名 b
分配給 my_table
的第二個例項,而第二個語句將別名分配給連線的結果
SELECT * FROM my_table AS a CROSS JOIN my_table AS b ... SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...
另一種表別名形式為表列以及表本身指定臨時名稱
FROMtable_reference
[AS]alias
(column1
[,column2
[, ...]] )
如果指定的列別名少於實際表中的列數,則其餘列不會重新命名。這種語法對於自連線或子查詢特別有用。
當別名應用於 JOIN
子句的輸出時,該別名會隱藏 JOIN
內的原始名稱。例如
SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...
是有效的 SQL,但是
SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c
無效;表別名 a
在別名 c
之外是不可見的。
指定派生表的子查詢必須用括號括起來。它們可以被指定表別名,並可選擇性地指定列別名(如 第 7.2.1.2 節)。例如
FROM (SELECT * FROM table1) AS alias_name
這個例子等同於 FROM table1 AS alias_name
。當子查詢涉及分組或聚合時,會出現更有趣的、無法簡化為普通連線的情況。
子查詢也可以是 VALUES
列表
FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow')) AS names(first, last)
同樣,表別名是可選的。為 VALUES
列表的列指定別名是可選的,但這是一個好習慣。有關更多資訊,請參見 第 7.7 節。
根據 SQL 標準,必須為子查詢提供表別名。 PostgreSQL 允許省略 AS
和別名,但在可能會移植到其他系統的 SQL 程式碼中,使用別名是一個好習慣。
表函式是產生一組行的函式,這些行由基本資料型別(標量型別)或複合資料型別(錶行)組成。它們在查詢的 FROM
子句中用作表、檢視或子查詢。表函式返回的列可以包含在 SELECT
, JOIN
, 或 WHERE
子句中,方式與表、檢視或子查詢的列相同。
表函式也可以使用 ROWS FROM
語法組合,結果以並行列的形式返回;在這種情況下,結果行的數量是最大的函式結果的數量,較小的結果用 null 值填充以匹配。
function_call
[WITH ORDINALITY] [[AS]table_alias
[(column_alias
[, ... ])]] ROWS FROM(function_call
[, ... ] ) [WITH ORDINALITY] [[AS]table_alias
[(column_alias
[, ... ])]]
如果指定了 WITH ORDINALITY
子句,則會在函式結果列中新增一個額外的 bigint
型別的列。此列對函式結果集中的行進行編號,從 1 開始。(這是 SQL 標準語法 UNNEST ... WITH ORDINALITY
的泛化。)預設情況下,序數列稱為 ordinality
,但可以使用 AS
子句為其指定不同的列名。
特殊表函式 UNNEST
可以使用任意數量的陣列引數呼叫,並且它返回相應數量的列,就好像 UNNEST
(參見 第 9.19 節)已在每個引數上單獨呼叫並使用 ROWS FROM
構造組合一樣。
UNNEST(array_expression
[, ... ] ) [WITH ORDINALITY] [[AS]table_alias
[(column_alias
[, ... ])]]
如果沒有指定 table_alias
,則函式名用作表名;對於 ROWS FROM()
構造,使用第一個函式的名字。
如果未提供列別名,則對於返回基本資料型別的函式,列名也與函式名相同。對於返回複合型別的函式,結果列獲得該型別各個屬性的名稱。
一些例子
CREATE TABLE foo (fooid int, foosubid int, fooname text); CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ SELECT * FROM foo WHERE fooid = $1; $$ LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1; SELECT * FROM foo WHERE foosubid IN ( SELECT foosubid FROM getfoo(foo.fooid) z WHERE z.fooid = foo.fooid ); CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1); SELECT * FROM vw_getfoo;
在某些情況下,定義可以根據呼叫方式返回不同列集的表函式是有用的。為了支援這一點,表函式可以被宣告為返回偽型別 record
,且沒有 OUT
引數。當此類函式在查詢中使用時,必須在查詢本身中指定預期的行結構,以便系統知道如何解析和規劃查詢。此語法看起來像
function_call
[AS]alias
(column_definition
[, ... ])function_call
AS [alias
] (column_definition
[, ... ]) ROWS FROM( ...function_call
AS (column_definition
[, ... ]) [, ... ] )
當不使用 ROWS FROM()
語法時,column_definition
列表會替換可以附加到 FROM
項的列別名列表;列定義中的名稱用作列別名。使用 ROWS FROM()
語法時,可以在每個成員函式上單獨附加 column_definition
列表;或者,如果只有一個成員函式且沒有 WITH ORDINALITY
子句,則可以在 ROWS FROM()
之後編寫 column_definition
列表來代替列別名列表。
考慮這個例子
SELECT * FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc') AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
dblink 函式(屬於 dblink 模組)執行遠端查詢。它被宣告為返回 record
,因為它可能用於任何型別的查詢。實際的列集必須在呼叫查詢中指定,以便解析器知道,例如,*
應該展開成什麼。
這個例子使用了 ROWS FROM
SELECT * FROM ROWS FROM ( json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]') AS (a INTEGER, b TEXT), generate_series(1, 3) ) AS x (p, q, s) ORDER BY p; p | q | s -----+-----+--- 40 | foo | 1 100 | bar | 2 | | 3
它將兩個函式組合成一個 FROM
目標。json_to_recordset()
被指示返回兩列,第一列是 integer
,第二列是 text
。generate_series()
的結果被直接使用。ORDER BY
子句按整數對列值進行排序。
LATERAL
子查詢 #出現在 FROM
中的子查詢可以被關鍵字 LATERAL
提前。這允許它們引用前面 FROM
項提供的列。(沒有 LATERAL
,每個子查詢都獨立計算,因此不能交叉引用任何其他 FROM
項。)
出現在 FROM
中的表函式也可以被關鍵字 LATERAL
提前,但對於函式來說,關鍵字是可選的;無論如何,函式的引數可以包含對前面 FROM
項提供的列的引用。
LATERAL
項可以出現在 FROM
列表的頂層,或者出現在 JOIN
樹中。在後一種情況下,它也可以引用位於它所處的 JOIN
的左側的任何項。
當 FROM
項包含 LATERAL
交叉引用時,求值過程如下:對於提供交叉引用的列的 FROM
項的每一行,或者提供列的多個 FROM
項的行集,LATERAL
項使用該行或行集的值計算列。結果行(或行集)與它們計算自的行正常連線。這對於來自列源表(或表集)的每一行或行集重複。
一個簡單的 LATERAL
示例是
SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;
這不太有用,因為它產生的結果與更常規的
SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;
完全相同。LATERAL
主要在交叉引用的列對於計算要連線的行是必需的時有用。一個常見的應用是為集合返回函式提供引數值。例如,假設 vertices(polygon)
返回多邊形的頂點集,我們可以透過以下方式識別表中儲存的多邊形中靠近的頂點
SELECT p1.id, p2.id, v1, v2 FROM polygons p1, polygons p2, LATERAL vertices(p1.poly) v1, LATERAL vertices(p2.poly) v2 WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;
此查詢也可以寫成
SELECT p1.id, p2.id, v1, v2 FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1, polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2 WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;
或以其他幾種等效的表述方式。(如前所述,在此示例中 LATERAL
關鍵字不是必需的,但我們為了清晰起見使用了它。)
通常,LEFT JOIN
到 LATERAL
子查詢會特別方便,這樣即使 LATERAL
子查詢沒有為源行生成任何行,這些源行也會出現在結果中。例如,如果 get_product_names()
返回製造商生產的產品的名稱,但我們表中的某些製造商當前不生產任何產品,我們可以像這樣找出是哪些製造商
SELECT m.name FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true WHERE pname IS NULL;
WHERE
子句 #WHERE
子句的語法是
WHERE search_condition
其中 search_condition
是任何值表示式(參見 第 4.2 節),該表示式返回 boolean
型別的值。
FROM
子句處理完成後,會檢查派生的虛擬表的每一行是否滿足搜尋條件。如果條件的結果為 true,則將該行保留在輸出表中,否則(即,如果結果為 false 或 null)將其丟棄。搜尋條件通常至少引用 FROM
子句中生成的表的一個列;這不是必需的,但否則 WHERE
子句將非常無用。
內部連線的連線條件可以寫在 WHERE
子句中,也可以寫在 JOIN
子句中。例如,這些表表達式是等效的
FROM a, b WHERE a.id = b.id AND b.val > 5
和
FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5
或者甚至
FROM a NATURAL JOIN b WHERE b.val > 5
您選擇哪種方式主要取決於風格。FROM
子句中的 JOIN
語法可能不像 SQL 標準中的那樣可移植到其他 SQL 資料庫管理系統。對於外部連線,沒有選擇:它們必須在 FROM
子句中完成。外部連線的 ON
或 USING
子句與 WHERE
條件 不等效,因為它會導致新增行(用於未匹配的輸入行)以及刪除最終結果中的行。
以下是 WHERE
子句的一些示例
SELECT ... FROM fdt WHERE c1 > 5 SELECT ... FROM fdt WHERE c1 IN (1, 2, 3) SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2) SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100 SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)
fdt
是在 FROM
子句中派生的表。不滿足 WHERE
子句搜尋條件的行將從 fdt
中刪除。注意標量子查詢作為值表示式的用法。與任何其他查詢一樣,子查詢可以採用複雜的表表達式。還要注意 fdt
在子查詢中是如何被引用的。將 c1
限定為 fdt.c1
僅在 c1
也是子查詢的派生輸入表的列名時才必需。但即使不需要,限定列名也能增加清晰度。此示例顯示了外部查詢的列命名範圍如何擴充套件到其內部查詢。
GROUP BY
和 HAVING
子句 #透過 WHERE
過濾後,派生的輸入表可能會進行分組,使用 GROUP BY
子句,並使用 HAVING
子句消除組行。
SELECTselect_list
FROM ... [WHERE ...] GROUP BYgrouping_column_reference
[,grouping_column_reference
]...
GROUP BY
子句用於將表中的行根據所有列中的值進行分組。列的列出順序無關緊要。其效果是將每個具有相同值的行集合併成一個代表該組所有行的組行。這樣做是為了消除輸出中的冗餘和/或計算適用於這些組的聚合。例如
=>
SELECT * FROM test1;
x | y ---+--- a | 3 c | 2 b | 5 a | 1 (4 rows)=>
SELECT x FROM test1 GROUP BY x;
x --- a b c (3 rows)
在第二個查詢中,我們不能寫 SELECT * FROM test1 GROUP BY x
,因為沒有一個 y
列的值可以與每個組關聯。分組的列可以在 select 列表中引用,因為它們在每個組中都有一個值。
一般來說,如果一個表被分組,不能出現在 GROUP BY
中的列只能在聚合表示式中引用。一個帶有聚合表示式的例子是
=>
SELECT x, sum(y) FROM test1 GROUP BY x;
x | sum ---+----- a | 4 b | 5 c | 2 (3 rows)
這裡 sum
是一個聚合函式,它在整個組上計算一個單一值。有關可用聚合函式的更多資訊,請參閱 第 9.21 節。
不帶聚合表示式的分組有效地計算列中唯一值的集合。這也可以透過 DISTINCT
子句來實現(參見 第 7.3.3 節)。
這裡是另一個例子:它計算每個產品的總銷售額(而不是所有產品的總銷售額)
SELECT product_id, p.name, (sum(s.units) * p.price) AS sales FROM products p LEFT JOIN sales s USING (product_id) GROUP BY product_id, p.name, p.price;
在此示例中,列 product_id
, p.name
, 和 p.price
必須在 GROUP BY
子句中,因為它們在查詢選擇列表中被引用(但見下文)。列 s.units
不必在 GROUP BY
列表中,因為它僅在聚合表示式(sum(...)
)中使用,該表示式代表產品的銷售額。對於每個產品,查詢返回一個關於該產品所有銷售的摘要行。
如果產品表設定的方式是,例如,product_id
是主鍵,那麼在上面的示例中,僅按 product_id
分組就足夠了,因為名稱和價格將 函式依賴 於產品 ID,因此對於每個產品 ID 組,將返回哪個名稱和價格值將沒有歧義。
在嚴格的 SQL 中,GROUP BY
只能按源表的列進行分組,但 PostgreSQL 將此擴充套件為也允許 GROUP BY
按選擇列表中的列進行分組。也可以允許按值表示式而不是簡單的列名進行分組。
如果一個表使用 GROUP BY
進行了分組,但只有某些組是感興趣的,則可以使用 HAVING
子句,就像 WHERE
子句一樣,從結果中消除組。語法是
SELECTselect_list
FROM ... [WHERE ...] GROUP BY ... HAVINGboolean_expression
HAVING
子句中的表示式可以同時引用分組表示式和非分組表示式(必然涉及聚合函式)。
示例:
=>
SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
x | sum ---+----- a | 4 b | 5 (2 rows)=>
SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
x | sum ---+----- a | 4 b | 5 (2 rows)
再次,一個更現實的例子
SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit FROM products p LEFT JOIN sales s USING (product_id) WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks' GROUP BY product_id, p.name, p.price, p.cost HAVING sum(p.price * s.units) > 5000;
在上面的示例中,WHERE
子句按非分組列選擇行(表示式僅對過去四周的銷售額為 true),而 HAVING
子句將輸出限制為總計銷售額超過 5000 的組。請注意,聚合表示式不必在查詢的所有部分都相同。
如果一個查詢包含聚合函式呼叫,但沒有 GROUP BY
子句,則仍然會發生分組:結果是單個組行(如果單行隨後被 HAVING
消除,則可能根本沒有行)。如果它包含 HAVING
子句,即使沒有聚合函式呼叫或 GROUP BY
子句,也是如此。
GROUPING SETS
, CUBE
, 和 ROLLUP
#使用 分組集 的概念,可以實現比上述更復雜的分組操作。由 FROM
和 WHERE
子句選擇的資料會為每個指定的組集分別分組,然後為每個組計算聚合,就像對於簡單的 GROUP BY
子句一樣,然後返回結果。例如
=>
SELECT * FROM items_sold;
brand | size | sales -------+------+------- Foo | L | 10 Foo | M | 20 Bar | M | 15 Bar | L | 5 (4 rows)=>
SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());
brand | size | sum -------+------+----- Foo | | 30 Bar | | 20 | L | 15 | M | 35 | | 50 (5 rows)
GROUPING SETS
的每個子列表都可以指定零個或多個列或表示式,並像直接在 GROUP BY
子句中一樣進行解釋。一個空的分組集意味著所有行都被聚合為單個組(即使沒有輸入行,也會輸出該組),如上所述,對於沒有 GROUP BY
子句的聚合函式的情況。
對分組列或表示式的引用會在結果行中替換為 null 值,這些行屬於該列未出現的組集。為了區分特定的輸出行是由哪個分組產生的,請參見 表 9.66。
提供了簡寫表示法來指定兩種常見的組集型別。形式為
ROLLUP (e1
,e2
,e3
, ... )
的子句表示給定的表示式列表以及列表的所有字首(包括空列表);因此它等同於
GROUPING SETS ( (e1
,e2
,e3
, ... ), ... (e1
,e2
), (e1
), ( ) )
這通常用於對分層資料進行分析;例如,按部門、部門和公司範圍的總計計算的工資。
形式為
CUBE (e1
,e2
, ... )
的子句表示給定的列表及其所有可能的子集(即冪集)。因此
CUBE ( a, b, c )
等同於
GROUPING SETS ( ( a, b, c ), ( a, b ), ( a, c ), ( a ), ( b, c ), ( b ), ( c ), ( ) )
CUBE
或 ROLLUP
子句的單個元素可以是單獨的表示式,或者括號中的元素子列表。在後一種情況下,子列表被視為單個單元,用於生成單個組集。例如
CUBE ( (a, b), (c, d) )
等同於
GROUPING SETS ( ( a, b, c, d ), ( a, b ), ( c, d ), ( ) )
和
ROLLUP ( a, (b, c), d )
等同於
GROUPING SETS ( ( a, b, c, d ), ( a, b, c ), ( a ), ( ) )
CUBE
和 ROLLUP
構造可以直接用在 GROUP BY
子句中,或者巢狀在 GROUPING SETS
子句中。如果一個 GROUPING SETS
子句巢狀在另一個 GROUPING SETS
子句中,效果就好像內部子句的所有元素都直接寫入了外部子句一樣。
如果在單個 GROUP BY
子句中指定了多個分組項,則最終的組集列表是個別項的笛卡爾積。例如
GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))
等同於
GROUP BY GROUPING SETS ( (a, b, c, d), (a, b, c, e), (a, b, d), (a, b, e), (a, c, d), (a, c, e), (a, d), (a, e) )
GROUP BY ROLLUP (a, b), ROLLUP (a, c)
等同於
GROUP BY GROUPING SETS ( (a, b, c), (a, b), (a, b), (a, c), (a), (a), (a, c), (a), () )
如果這些重複項是不希望的,可以使用 DISTINCT
子句直接從 GROUP BY
中移除它們。因此
GROUP BY DISTINCT ROLLUP (a, b), ROLLUP (a, c)
等同於
GROUP BY GROUPING SETS ( (a, b, c), (a, b), (a, c), (a), () )
這與使用 SELECT DISTINCT
不同,因為輸出行可能仍然包含重複項。如果任何未分組的列包含 NULL,它將無法與當同一列被分組時使用的 NULL 區分開。
(a, b)
構造在表示式中通常被識別為 行構造器。在 GROUP BY
子句中,這不適用於表示式的頂層,並且 (a, b)
被解析為如上所述的表示式列表。如果您出於某種原因 需要 在分組表示式中使用行構造器,請使用 ROW(a, b)
。
如果查詢包含任何視窗函式(參見 第 3.5 節, 第 9.22 節 和 第 4.2.8 節),則這些函式將在進行任何分組、聚合和 HAVING
過濾之後進行評估。也就是說,如果查詢使用了任何聚合、GROUP BY
或 HAVING
,那麼視窗函式看到的行將是組行,而不是來自 FROM
/WHERE
的原始錶行。
當使用多個視窗函式時,所有具有等效 PARTITION BY
和 ORDER BY
子句的視窗函式在其視窗定義中,保證看到相同順序的輸入行,即使 ORDER BY
沒有唯一確定順序。但是,不保證評估具有不同 PARTITION BY
或 ORDER BY
規範的函式。(在這種情況下,通常需要在視窗函式求值之間進行排序步驟,並且排序不保證保留其 ORDER BY
視為等效的行順序。)
目前,視窗函式總是需要預先排序的資料,因此查詢輸出將按照一個或另一個視窗函式的 PARTITION BY
/ORDER BY
子句進行排序。但是,不建議依賴此行為。如果希望確保結果以特定方式排序,請使用顯式的頂級 ORDER BY
子句。
如果您在文件中看到任何不正確的內容,與您對特定功能的經驗不符,或需要進一步澄清,請使用 此表單 報告文件問題。