SELECT、TABLE、WITH — 從資料表或視窗檢索列
[ WITH [ RECURSIVE ]with_query
[, ...] ] SELECT [ ALL | DISTINCT [ ON (expression
[, ...] ) ] ] [ { * |expression
[ [ AS ]output_name
] } [, ...] ] [ FROMfrom_item
[, ...] ] [ WHEREcondition
] [ GROUP BY [ ALL | DISTINCT ]grouping_element
[, ...] ] [ HAVINGcondition
] [ WINDOWwindow_name
AS (window_definition
) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ]select
] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [ LIMIT {count
| ALL } ] [ OFFSETstart
[ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [count
] { ROW | ROWS } { ONLY | WITH TIES } ] [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OFfrom_reference
[, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ] wherefrom_item
can be one of: [ ONLY ]table_name
[ * ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ TABLESAMPLEsampling_method
(argument
[, ...] ) [ REPEATABLE (seed
) ] ] [ LATERAL ] (select
) [ [ AS ]alias
[ (column_alias
[, ...] ) ] ]with_query_name
[ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ LATERAL ]function_name
( [argument
[, ...] ] ) [ WITH ORDINALITY ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ] [ LATERAL ]function_name
( [argument
[, ...] ] ) [ AS ]alias
(column_definition
[, ...] ) [ LATERAL ]function_name
( [argument
[, ...] ] ) AS (column_definition
[, ...] ) [ LATERAL ] ROWS FROM(function_name
( [argument
[, ...] ] ) [ AS (column_definition
[, ...] ) ] [, ...] ) [ WITH ORDINALITY ] [ [ AS ]alias
[ (column_alias
[, ...] ) ] ]from_item
join_type
from_item
{ ONjoin_condition
| USING (join_column
[, ...] ) [ ASjoin_using_alias
] }from_item
NATURALjoin_type
from_item
from_item
CROSS JOINfrom_item
andgrouping_element
can be one of: ( )expression
(expression
[, ...] ) ROLLUP ( {expression
| (expression
[, ...] ) } [, ...] ) CUBE ( {expression
| (expression
[, ...] ) } [, ...] ) GROUPING SETS (grouping_element
[, ...] ) andwith_query
is:with_query_name
[ (column_name
[, ...] ) ] AS [ [ NOT ] MATERIALIZED ] (select
|values
|insert
|update
|delete
|merge
) [ SEARCH { BREADTH | DEPTH } FIRST BYcolumn_name
[, ...] SETsearch_seq_col_name
] [ CYCLEcolumn_name
[, ...] SETcycle_mark_col_name
[ TOcycle_mark_value
DEFAULTcycle_mark_default
] USINGcycle_path_col_name
] TABLE [ ONLY ]table_name
[ * ]
SELECT
從零個或多個資料表檢索列。 SELECT
的一般處理流程如下:
會計算 WITH
列表中的所有查詢。 這些查詢實際上充當可在 FROM
列表中引用的臨時資料表。 在 FROM
中被引用多次的 WITH
查詢只會計算一次,除非使用 NOT MATERIALIZED
另行指定。(請參閱下方的WITH 子句。)
會計算 FROM
列表中的所有元素。(FROM
列表中的每個元素都是真實或虛擬資料表。) 如果在 FROM
列表中指定了多個元素,則它們會交叉聯結在一起。(請參閱下方的FROM 子句。)
如果指定了 WHERE
子句,則會從輸出中消除所有不符合條件的列。(請參閱下方的WHERE 子句。)
如果指定了 GROUP BY
子句,或者存在彙總函數呼叫,則會將輸出合併到符合一個或多個值的列群組中,並計算彙總函數的結果。 如果存在 HAVING
子句,則它會消除不符合給定條件的群組。(請參閱下方的GROUP BY 子句和HAVING 子句。) 雖然查詢輸出欄名義上是在下一個步驟中計算的,但也可以在 GROUP BY
子句中引用它們(按名稱或序號)。
實際的輸出列是使用每個選定列或列群組的 SELECT
輸出運算式來計算的。(請參閱下方的SELECT 列表。)
SELECT DISTINCT
從結果中消除重複的列。 SELECT DISTINCT ON
消除所有指定運算式都符合的列。 SELECT ALL
(預設值)將傳回所有候選列,包括重複列。(請參閱下方的DISTINCT 子句。)
使用運算子 UNION
、INTERSECT
和 EXCEPT
,可以合併多個 SELECT
陳述式的輸出,以形成單一結果集。 UNION
運算子會傳回一個或兩個結果集中存在的所有列。 INTERSECT
運算子會傳回嚴格地存在於兩個結果集中的所有列。 EXCEPT
運算子會傳回第一個結果集中存在但第二個結果集中不存在的列。 在所有三種情況下,除非指定了 ALL
,否則會消除重複的列。 可以新增雜訊詞 DISTINCT
來明確指定消除重複的列。 請注意,即使 ALL
是 SELECT
本身的預設行為,但 DISTINCT
在這裡也是預設行為。(請參閱下方的UNION 子句、INTERSECT 子句和EXCEPT 子句。)
如果指定了 ORDER BY
子句,則傳回的列會依指定的順序排序。 如果未給定 ORDER BY
,則會以系統認為最快產生結果的任何順序傳回列。(請參閱下方的ORDER BY 子句。)
如果指定了 LIMIT
(或 FETCH FIRST
)或 OFFSET
子句,則 SELECT
陳述式只會傳回結果列的子集。(請參閱下方的LIMIT 子句。)
如果指定了 FOR UPDATE
、FOR NO KEY UPDATE
、FOR SHARE
或 FOR KEY SHARE
,則 SELECT
陳述式會鎖定選定的列,以防止並行更新。(請參閱下方的鎖定子句。)
您必須對 SELECT
命令中使用的每個欄位擁有 SELECT
權限。 使用 FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或 FOR KEY SHARE
也需要 UPDATE
權限(至少針對每個選定的資料表中的一個欄位)。
WITH
子句WITH
子句允許您指定一個或多個子查詢,這些子查詢可以在主查詢中以名稱引用。這些子查詢實際上充當主查詢期間的臨時表或視窗。每個子查詢都可以是 SELECT
、TABLE
、VALUES
、INSERT
、UPDATE
、DELETE
或 MERGE
語句。在 WITH
中編寫資料修改語句(INSERT
、UPDATE
、DELETE
或 MERGE
)時,通常包含 RETURNING
子句。形成臨時表並由主查詢讀取的是 RETURNING
的輸出,而非 語句修改的底層表。如果省略 RETURNING
,該語句仍然會執行,但不產生任何輸出,因此無法作為表被主查詢引用。
必須為每個 WITH
查詢指定一個名稱(沒有綱要限定)。您可以選擇指定欄位名稱列表;如果省略此列表,則欄位名稱將從子查詢中推斷。
如果指定了 RECURSIVE
,則允許 SELECT
子查詢按名稱引用自身。這樣的子查詢必須具有以下形式
non_recursive_term
UNION [ ALL | DISTINCT ]recursive_term
遞迴自引用必須出現在 UNION
的右側。每個查詢僅允許一個遞迴自引用。不支援遞迴資料修改語句,但您可以在資料修改語句中使用遞迴 SELECT
查詢的結果。有關範例,請參閱第 7.8 節。
RECURSIVE
的另一個效果是 WITH
查詢不必排序:一個查詢可以引用列表中稍後的另一個查詢。(但是,未實現循環引用或相互遞迴。)如果沒有 RECURSIVE
,WITH
查詢只能引用 WITH
列表中較早的同級 WITH
查詢。
當 WITH
子句中有多個查詢時,RECURSIVE
應僅編寫一次,緊接在 WITH
之後。它適用於 WITH
子句中的所有查詢,儘管它對不使用遞迴或向前引用的查詢沒有影響。
可選的 SEARCH
子句會計算一個搜尋序列欄位,該欄位可用於以廣度優先或深度優先的順序對遞迴查詢的結果進行排序。提供的欄位名稱列表指定用於追蹤已訪問列的列鍵。名為 search_seq_col_name
的欄位將新增至 WITH
查詢的結果欄位列表。可以在外部查詢中按此欄位進行排序,以實現相應的排序。有關範例,請參閱第 7.8.2.1 節。
可選的 CYCLE
子句用於偵測遞迴查詢中的循環。提供的欄位名稱列表指定用於追蹤已訪問列的列鍵。名為 cycle_mark_col_name
的欄位將新增至 WITH
查詢的結果欄位列表。偵測到循環時,此欄位將設定為 cycle_mark_value
,否則設定為 cycle_mark_default
。此外,偵測到循環時,將停止遞迴聯合的處理。cycle_mark_value
和 cycle_mark_default
必須是常數,並且它們必須可以強制轉換為通用資料類型,並且資料類型必須具有不相等運算符。(SQL 標準要求它們是布林常數或字元串,但 PostgreSQL 並不要求。)預設情況下,使用 TRUE
和 FALSE
(類型為 boolean
)。此外,名為 cycle_path_col_name
的欄位將新增至 WITH
查詢的結果欄位列表。此欄位在內部用於追蹤已訪問的列。有關範例,請參閱第 7.8.2.2 節。
SEARCH
和 CYCLE
子句僅對遞迴 WITH
查詢有效。with_query
必須是兩個 SELECT
(或等效)指令的 UNION
(或 UNION ALL
)(沒有巢狀 UNION
)。如果同時使用這兩個子句,則 SEARCH
子句新增的欄位會出現在 CYCLE
子句新增的欄位之前。
主查詢和 WITH
查詢都(概念上)同時執行。這意味著無法從查詢的其他部分看到 WITH
中資料修改語句的效果,除非讀取其 RETURNING
輸出。如果兩個這樣的資料修改語句嘗試修改同一列,則結果是不確定的。
WITH
查詢的一個關鍵屬性是,它們通常每個主查詢執行僅評估一次,即使主查詢多次引用它們也是如此。特別是,保證資料修改語句執行一次且僅執行一次,無論主查詢是否讀取其所有或任何輸出。
但是,可以將 WITH
查詢標記為 NOT MATERIALIZED
以移除此保證。在這種情況下,WITH
查詢可以折疊到主查詢中,就像它是主查詢的 FROM
子句中的簡單子 SELECT
一樣。如果主查詢多次引用該 WITH
查詢,則會導致重複計算;但是,如果每個此類使用僅需要 WITH
查詢的總輸出中的少量列,則 NOT MATERIALIZED
可以通過允許聯合優化查詢來提供淨節省。如果 NOT MATERIALIZED
附加到遞迴或非副作用的 WITH
查詢(即,不是包含任何不穩定函式的簡單 SELECT
),則會被忽略。
預設情況下,如果副作用的 WITH
查詢在主查詢的 FROM
子句中使用恰好一次,則會將其折疊到主查詢中。這允許在語義上應該不可見的情況下聯合優化兩個查詢層級。但是,可以通過將 WITH
查詢標記為 MATERIALIZED
來防止這種折疊。例如,如果 WITH
查詢用作優化障礙,以防止規劃器選擇錯誤的計劃,則這可能很有用。 v12 之前的 PostgreSQL 版本從未進行過這種折疊,因此為較舊版本編寫的查詢可能依賴於 WITH
來充當優化障礙。
有關更多資訊,請參閱第 7.8 節。
FROM
子句FROM
子句指定 SELECT
的一個或多個來源表格。如果指定了多個來源,結果將是所有來源的笛卡爾積(交叉聯結)。但通常會加入限定條件(透過 WHERE
子句)來將傳回的列限制為笛卡爾積的一個小子集。
FROM
子句可以包含以下元素:
table_name
現有表格或檢視表的名稱(可選擇性地使用 schema 限定)。如果在表格名稱之前指定了 ONLY
,則只會掃描該表格。如果未指定 ONLY
,則會掃描該表格及其所有子表格(如果有的話)。可選擇性地在表格名稱後指定 *
,以明確表示包含子表格。
alias
包含別名的 FROM
項目的替代名稱。別名用於簡潔或消除自聯結的歧義(在自聯結中,同一個表格會被掃描多次)。當提供別名時,它會完全隱藏表格或函數的實際名稱;例如,給定 FROM foo AS f
,SELECT
的其餘部分必須將此 FROM
項目稱為 f
,而不是 foo
。如果寫了別名,也可以寫入欄位別名清單,以提供表格的一個或多個欄位的替代名稱。
TABLESAMPLE sampling_method
( argument
[, ...] ) [ REPEATABLE ( seed
) ]
在 table_name
之後的 TABLESAMPLE
子句表示應該使用指定的 sampling_method
來檢索該表格中的列的子集。此取樣先於任何其他篩選器(例如 WHERE
子句)的應用。標準 PostgreSQL 發行版包含兩種取樣方法,BERNOULLI
和 SYSTEM
,並且可以透過擴充功能在資料庫中安裝其他取樣方法。
BERNOULLI
和 SYSTEM
取樣方法都接受單個 argument
,該參數是要取樣的表格比例,表示為 0 到 100 之間的百分比。此參數可以是任何 real
數值表達式。(其他取樣方法可能會接受更多或不同的參數。)這兩種方法都各自傳回表格的隨機選擇樣本,該樣本將包含大約指定百分比的表格列。BERNOULLI
方法掃描整個表格,並以指定的機率獨立選擇或忽略個別列。SYSTEM
方法執行區塊層級取樣,每個區塊都有指定的被選中機率;傳回每個選定區塊中的所有列。當指定小的取樣百分比時,SYSTEM
方法比 BERNOULLI
方法快得多,但由於叢集效應,它可能會傳回表格的較不隨機樣本。
可選的 REPEATABLE
子句指定用於在取樣方法中產生隨機數的 seed
數字或表達式。種子值可以是任何非 null 的浮點數值。如果表格在此期間沒有變更,則兩個指定相同種子和 argument
值的查詢將選擇相同的表格樣本。但是,不同的種子值通常會產生不同的樣本。如果未給定 REPEATABLE
,則每次查詢都會根據系統產生的種子選擇一個新的隨機樣本。請注意,某些附加取樣方法不接受 REPEATABLE
,並且每次使用都會始終產生新的樣本。
select
子 SELECT
可以出現在 FROM
子句中。這就像它的輸出被建立為此單個 SELECT
命令期間的暫存表格一樣。請注意,子 SELECT
必須用括號括起來,並且可以像表格一樣提供別名。此處也可以使用 VALUES
命令。
with_query_name
透過寫入 WITH
查詢的名稱來引用它,就像查詢的名稱是表格名稱一樣。(事實上,對於主要查詢的目的而言,WITH
查詢會隱藏任何同名的真實表格。如有必要,您可以透過 schema 限定表格的名稱來引用同名的真實表格。)可以像表格一樣提供別名。
function_name
函數呼叫可以出現在 FROM
子句中。(這對於傳回結果集的函數特別有用,但可以使用任何函數。)這就像函數的輸出被建立為此單個 SELECT
命令期間的暫存表格一樣。如果函數的結果類型是複合類型(包括具有多個 OUT
參數的函數的情況),則每個屬性都會成為隱含表格中的一個單獨欄位。
當將可選的 WITH ORDINALITY
子句新增到函數呼叫時,類型為 bigint
的附加欄位將附加到函數的結果欄位。此欄位會對函數結果集的列進行編號,從 1 開始。預設情況下,此欄位的名稱為 ordinality
。
可以像表格一樣提供別名。如果寫了別名,也可以寫入欄位別名清單,以提供函數的複合傳回類型(包括 ordinality 欄位,如果存在)的一個或多個屬性的替代名稱。
可以透過用 ROWS FROM( ... )
包圍多個函數呼叫來將它們組合到單個 FROM
子句項目中。此類項目的輸出是每個函數的第一列的串聯,然後是每個函數的第二列的串聯,依此類推。如果某些函數產生的列數少於其他函數,則會用 null 值替換遺失的資料,以便傳回的總列數始終與產生最多列的函數相同。
如果函式定義為傳回 record
資料類型,則必須存在別名或關鍵字 AS
,後接欄位定義清單,格式為 (
。欄位定義清單必須符合函式傳回的欄位數量和類型。column_name
data_type
[, ... ])
當使用 ROWS FROM( ... )
語法時,如果其中一個函式需要欄位定義清單,最好將欄位定義清單放在 ROWS FROM( ... )
內的函式呼叫之後。只有在只有單一函式且沒有 WITH ORDINALITY
子句時,才能將欄位定義清單放在 ROWS FROM( ... )
建構之後。
要將 ORDINALITY
與欄位定義清單一起使用,您必須使用 ROWS FROM( ... )
語法,並將欄位定義清單放在 ROWS FROM( ... )
內。
join_type (連接類型)
以下之一:
[ INNER ] JOIN (內連接)
LEFT [ OUTER ] JOIN (左外部連接)
RIGHT [ OUTER ] JOIN (右外部連接)
FULL [ OUTER ] JOIN (完整外部連接)
對於 INNER
和 OUTER
連接類型,必須指定連接條件,也就是 ON
、join_condition
USING (
或 join_column
[, ...])NATURAL
三者之一。有關含義,請參閱下文。
JOIN
子句組合了兩個 FROM
項目,為了方便起見,我們將它們稱為“表格”,但實際上它們可以是任何類型的 FROM
項目。如有必要,請使用括號來確定巢狀結構的順序。在沒有括號的情況下,JOIN
會從左到右巢狀排列。在任何情況下,JOIN
的綁定都比分隔 FROM
清單項目的逗號更緊密。所有 JOIN
選項只是一種符號上的便利,因為它們所做的事情,您都可以使用純粹的 FROM
和 WHERE
來完成。
LEFT OUTER JOIN
會傳回合格笛卡爾乘積中的所有列(即,所有通過其連接條件的組合列),以及左側表格中沒有通過連接條件的右側列的每一列的副本。此左側列通過插入右側欄位的 null 值來擴展到連接表格的完整寬度。請注意,在決定哪些列匹配時,只會考慮 JOIN
子句自身的條件。外部條件在之後應用。
相反地,RIGHT OUTER JOIN
會傳回所有連接的列,以及每個不匹配的右側列(在左側擴展 null 值)。這只是一種符號上的便利,因為您可以通過切換左右表格將其轉換為 LEFT OUTER JOIN
。
FULL OUTER JOIN
會傳回所有連接的列,以及每個不匹配的左側列(在右側擴展 null 值),以及每個不匹配的右側列(在左側擴展 null 值)。
ON join_condition
join_condition
是一個表達式,其結果是 boolean
類型的值(類似於 WHERE
子句),用於指定連接中哪些列被認為是匹配的。
USING ( join_column
[, ...] ) [ AS join_using_alias
]
形式為 USING ( a, b, ... )
的子句是 ON left_table.a = right_table.a AND left_table.b = right_table.b ...
的簡寫。此外,USING
暗示只會包含每對等效欄位中的一個,而不是兩個都包含在連接輸出中。
如果指定了 join_using_alias
名稱,它會為連接欄位提供一個表格別名。只有 USING
子句中列出的連接欄位才能通過此名稱尋址。與常規 alias
不同,這不會從查詢的其餘部分隱藏連接表格的名稱。此外,與常規 alias
不同,您不能編寫欄位別名清單 — 連接欄位的輸出名稱與它們在 USING
清單中顯示的名稱相同。
NATURAL
NATURAL
是 USING
清單的簡寫,該清單提及兩個表格中所有具有匹配名稱的欄位。如果沒有常見的欄位名稱,NATURAL
相當於 ON TRUE
。
CROSS JOIN (交叉連接)
CROSS JOIN
相當於 INNER JOIN ON (TRUE)
,也就是說,沒有列會因資格而移除。它們產生一個簡單的笛卡爾乘積,與您從 FROM
的頂層列出兩個表格獲得的結果相同,但受到連接條件的限制(如果有的話)。
LATERAL
LATERAL
關鍵字可以放在子-SELECT
FROM
項目之前。這允許子-SELECT
引用 FROM
清單中出現在它之前的 FROM
項目的欄位。(如果沒有 LATERAL
,每個子-SELECT
都會獨立評估,因此無法交叉引用任何其他 FROM
項目。)
LATERAL
也可以放在函式呼叫 FROM
項目之前,但在這種情況下,它是一個噪音詞,因為函式表達式在任何情況下都可以引用之前的 FROM
項目。
LATERAL
項目可以出現在 FROM
清單的頂層,或在 JOIN
樹中。在後一種情況下,它也可以引用任何位於它右側的 JOIN
的左側的項目。
當 FROM
項目包含 LATERAL
交叉引用時,評估按如下方式進行:對於提供交叉引用欄位的 FROM
項目的每一列,或者提供這些欄位的多個 FROM
項目的列集合,使用該列或列集合的欄位值來評估 LATERAL
項目。產生的列會像往常一樣與計算它們所依據的列連接。對於來自欄位源表格的每一列或列集合,都會重複此過程。
欄位來源資料表必須使用 INNER
或 LEFT
Join 方式連接到 LATERAL
項目,否則將不會有明確定義的資料列集合,從而無法為 LATERAL
項目計算每個資料列集合。 因此,雖然像
這樣的結構在語法上有效,但實際上不允許 X
RIGHT JOIN LATERAL Y
Y
參考 X
。
WHERE
子句可選的 WHERE
子句具有以下一般形式:
WHERE condition
其中 condition
是任何評估結果為 boolean
類型的運算式。 任何不滿足此條件的資料列將從輸出中排除。 如果將實際資料列值代入任何變數參考後,運算式返回 true,則該資料列滿足條件。
GROUP BY
子句可選的 GROUP BY
子句具有以下一般形式:
GROUP BY [ ALL | DISTINCT ] grouping_element
[, ...]
GROUP BY
將把所有針對分組運算式具有相同值的選定資料列壓縮成單個資料列。 在 grouping_element
內使用的 expression
可以是輸入欄位名稱、輸出欄位(SELECT
清單項目)的名稱或序號,或者是由輸入欄位值組成的任意運算式。 如果出現不明確的情況,GROUP BY
名稱將被解釋為輸入欄位名稱,而不是輸出欄位名稱。
如果 GROUPING SETS
、ROLLUP
或 CUBE
作為分組元素存在,則整個 GROUP BY
子句定義了一些獨立的 grouping sets
。 其效果等同於在子查詢之間建構一個 UNION ALL
,其中每個子查詢的分組集合作為其 GROUP BY
子句。 可選的 DISTINCT
子句在處理之前刪除重複的集合;它不會將 UNION ALL
轉換為 UNION DISTINCT
。 有關分組集合處理的更多詳細資訊,請參閱第 7.2.4 節。
如果有使用聚合函數,則會對組成每個群組的所有資料列計算聚合函數,從而為每個群組產生一個單獨的值。(如果存在聚合函數但沒有 GROUP BY
子句,則查詢會被視為具有一個包含所有選定資料列的單一群組。)通過將 FILTER
子句附加到聚合函數調用,可以進一步篩選傳遞給每個聚合函數的資料列集合;有關更多資訊,請參閱第 4.2.7 節。如果存在 FILTER
子句,則只有與其匹配的資料列才會包含在該聚合函數的輸入中。
當存在 GROUP BY
或任何聚合函數時,SELECT
清單運算式引用未分組的欄位是無效的,除非在聚合函數中,或者未分組的欄位在功能上依賴於分組的欄位,因為否則未分組的欄位將有多個可能返回的值。如果分組的欄位(或其子集)是包含未分組欄位的資料表的主鍵,則存在功能依賴關係。
請記住,所有聚合函數都會在評估 HAVING
子句或 SELECT
清單中的任何 “純量” 運算式之前進行評估。這意味著,例如,不能使用 CASE
運算式來跳過聚合函數的評估; 請參閱第 4.2.14 節。
目前,無法將 FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
與 GROUP BY
一起使用。
HAVING
子句可選的 HAVING
子句具有以下一般形式:
HAVING condition
其中 condition
與 WHERE
子句中指定的條件相同。
HAVING
刪除不滿足條件的群組資料列。HAVING
與 WHERE
不同:WHERE
在應用 GROUP BY
之前篩選個別資料列,而 HAVING
篩選由 GROUP BY
建立的群組資料列。 condition
中引用的每個欄位都必須明確引用分組欄位,除非該參考出現在聚合函數中,或者未分組的欄位在功能上依賴於分組欄位。
即使沒有 GROUP BY
子句,HAVING
的存在也會將查詢轉換為分組查詢。 這與查詢包含聚合函數但沒有 GROUP BY
子句時發生的情況相同。 所有選定的資料列都被認為形成單一群組,並且 SELECT
清單和 HAVING
子句只能從聚合函數中引用資料表欄位。 如果 HAVING
條件為 true,則此類查詢將發出單個資料列;如果條件為 false,則發出零個資料列。
目前,無法將 FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
與 HAVING
一起使用。
WINDOW
子句可選的 WINDOW
子句具有以下一般形式:
WINDOWwindow_name
AS (window_definition
) [, ...]
其中 window_name
是一個可以從 OVER
子句或後續視窗定義中參考的名稱,而 window_definition
是:
[existing_window_name
] [ PARTITION BYexpression
[, ...] ] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [frame_clause
]
如果指定了 existing_window_name
,則它必須參考 WINDOW
清單中的先前條目;新視窗會從該條目複製其分割子句,以及其排序子句(如果有的話)。 在這種情況下,新視窗無法指定其自己的 PARTITION BY
子句,並且只有在複製的視窗沒有 ORDER BY
子句時,它才能指定 ORDER BY
子句。 新視窗始終使用其自己的框架子句;複製的視窗不得指定框架子句。
PARTITION BY
清單的元素以與GROUP BY
子句的元素大致相同的方式進行解釋,除了它們始終是簡單的運算式,而不是輸出欄位的名稱或序號。另一個不同之處是,這些運算式可以包含聚合函數調用,這在常規GROUP BY
子句中是不允許的。允許這樣做是因為視窗化發生在分組和聚合之後。
類似地,ORDER BY
清單的元素以與語句級別的 ORDER BY
子句的元素大致相同的方式進行解釋,除了這些運算式始終被視為簡單的運算式,而不是輸出欄位的名稱或序號。
可選的 frame_clause
定義了視窗函數的視窗框架,這些函數依賴於框架(並非所有函數都依賴)。 視窗框架是查詢中每個資料列(稱為目前資料列)的一組相關資料列。 frame_clause
可以是以下之一:
{ RANGE | ROWS | GROUPS }frame_start
[frame_exclusion
] { RANGE | ROWS | GROUPS } BETWEENframe_start
ANDframe_end
[frame_exclusion
]
其中 frame_start
和 frame_end
可以是以下之一:
UNBOUNDED PRECEDINGoffset
PRECEDING CURRENT ROWoffset
FOLLOWING UNBOUNDED FOLLOWING
而 frame_exclusion
可以是以下之一:
EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
如果省略 frame_end
,則預設為 CURRENT ROW
。限制是 frame_start
不能是 UNBOUNDED FOLLOWING
,frame_end
不能是 UNBOUNDED PRECEDING
,並且 frame_end
的選擇不能在上述 frame_start
和 frame_end
選項列表中,比 frame_start
的選擇更早出現 — 例如,RANGE BETWEEN CURRENT ROW AND
是不允許的。offset
PRECEDING
預設的框架選項是 RANGE UNBOUNDED PRECEDING
,這與 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
相同;它將框架設定為從分割區的開頭到目前列的最後一個同層級(window 的 ORDER BY
子句認為與目前列等價的列;如果沒有 ORDER BY
,則所有列都是同層級)。一般來說,UNBOUNDED PRECEDING
表示框架從分割區的第一列開始,類似地,UNBOUNDED FOLLOWING
表示框架以分割區的最後一列結束,無論是 RANGE
、ROWS
還是 GROUPS
模式。在 ROWS
模式下,CURRENT ROW
表示框架從目前列開始或結束;但在 RANGE
或 GROUPS
模式下,它表示框架以 ORDER BY
排序中目前列的第一個或最後一個同層級開始或結束。offset
PRECEDING
和 offset
FOLLOWING
選項的含義因框架模式而異。在 ROWS
模式下,offset
是一個整數,表示框架從目前列之前或之後的多少列開始或結束。在 GROUPS
模式下,offset
是一個整數,表示框架從目前列的同層級群組之前或之後的多少個同層級群組開始或結束,其中同層級群組是根據 window 的 ORDER BY
子句等價的列群組。在 RANGE
模式下,使用 offset
選項需要 window 定義中只有一個 ORDER BY
欄位。然後,框架包含那些排序欄位值不超過 offset
小於(對於 PRECEDING
)或大於(對於 FOLLOWING
)目前列的排序欄位值的列。在這些情況下,offset
表達式的資料類型取決於排序欄位的資料類型。對於數值排序欄位,它通常與排序欄位的類型相同,但對於日期時間排序欄位,它是一個 interval
。在所有這些情況下,offset
的值必須是非 null 且非負數。此外,雖然 offset
不必是簡單的常數,但它不能包含變數、聚合函數或 window 函數。
frame_exclusion
選項允許將目前列周圍的列從框架中排除,即使它們根據框架開始和框架結束選項應該包含在內。EXCLUDE CURRENT ROW
從框架中排除目前列。EXCLUDE GROUP
從框架中排除目前列及其排序同層級。EXCLUDE TIES
從框架中排除目前列的任何同層級,但不排除目前列本身。EXCLUDE NO OTHERS
只是明確指定不排除目前列或其同層級的預設行為。
請注意,如果 ORDER BY
排序沒有唯一地對列進行排序,則 ROWS
模式可能會產生不可預測的結果。RANGE
和 GROUPS
模式旨在確保在 ORDER BY
排序中是同層級的列以相同的方式處理:給定同層級群組的所有列將包含在框架中或從中排除。
WINDOW
子句的目的是指定出現在查詢的視窗函數的行為,這些函數出現在查詢的SELECT
列表或ORDER BY
子句中。這些函數可以透過名稱在其 OVER
子句中參考 WINDOW
子句條目。但是,WINDOW
子句條目不必在任何地方被參考;如果它沒有在查詢中使用,它會被簡單地忽略。可以在沒有任何 WINDOW
子句的情況下使用視窗函數,因為視窗函數呼叫可以直接在其 OVER
子句中指定其視窗定義。但是,當多個視窗函數需要相同的視窗定義時,WINDOW
子句可以節省輸入。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能與 WINDOW
一起指定。
SELECT
列表SELECT
列表(在關鍵字 SELECT
和 FROM
之間)指定構成 SELECT
語句輸出列的表達式。這些表達式可以(並且通常確實會)參考在 FROM
子句中計算的欄位。
如同表格一樣,SELECT
的每個輸出欄位都有一個名稱。在簡單的 SELECT
語句中,這個名稱僅用於標記欄位以供顯示,但當 SELECT
是一個較大查詢的子查詢時,該名稱會被較大的查詢視為子查詢所產生的虛擬表格的欄位名稱。要指定輸出欄位的名稱,請在欄位的表達式之後寫入 AS
output_name
。(您可以省略 AS
,但前提是所需的輸出名稱與任何 PostgreSQL 關鍵字不符 (請參閱附錄 C)。為了防止將來可能新增的關鍵字,建議您始終撰寫 AS
或用雙引號括住輸出名稱。) 如果您未指定欄位名稱,PostgreSQL 會自動選擇一個名稱。如果欄位的表達式是一個簡單的欄位引用,那麼選擇的名稱與該欄位的名稱相同。在更複雜的情況下,可能會使用函數或類型名稱,或者系統可能會退回到諸如 ?column?
之類的生成名稱。
輸出欄位的名稱可用於在 ORDER BY
和 GROUP BY
子句中引用欄位的值,但不能在 WHERE
或 HAVING
子句中使用;在這些子句中,您必須寫出表達式。
可以將 *
寫在輸出列表中,而不是表達式,作為所選列的所有欄位的簡寫。此外,您可以將
寫為僅來自該表格的欄位的簡寫。在這些情況下,無法使用 table_name
.*AS
指定新名稱;輸出欄位名稱將與表格欄位名稱相同。
根據 SQL 標準,輸出列表中的表達式應在應用 DISTINCT
、ORDER BY
或 LIMIT
之前計算。這在使用 DISTINCT
時顯然是必要的,因為否則不清楚哪些值是不同的。但是,在許多情況下,如果在 ORDER BY
和 LIMIT
之後計算輸出表達式會很方便;特別是如果輸出列表包含任何不穩定或昂貴的函數。在這種行為下,函數求值的順序更直觀,並且不會有對應於從未出現在輸出中的行的求值。PostgreSQL 將有效地在排序和限制之後評估輸出表達式,只要這些表達式未在 DISTINCT
、ORDER BY
或 GROUP BY
中引用即可。(作為一個反例,SELECT f(x) FROM tab ORDER BY 1
顯然必須在排序之前評估 f(x)
。) 包含集合回傳函數的輸出表達式在排序之後和限制之前有效地進行評估,因此 LIMIT
將作用於截斷集合回傳函數的輸出。
9.6 之前的 PostgreSQL 版本未提供有關輸出表達式與排序和限制的評估時間的任何保證;它取決於所選查詢計劃的形式。
DISTINCT
子句如果指定了 SELECT DISTINCT
,則所有重複的列都會從結果集中刪除(每個重複群組保留一個列)。SELECT ALL
指定相反的情況:保留所有列;這是預設值。
SELECT DISTINCT ON (
僅保留每組列中給定表達式評估為相等的列的第一列。expression
[, ...] )DISTINCT ON
表達式使用與 ORDER BY
相同的規則進行解釋(請參閱上文)。請注意,除非使用 ORDER BY
確保所需的列首先出現,否則每組列的“第一列”是不可預測的。例如
SELECT DISTINCT ON (location) location, time, report FROM weather_reports ORDER BY location, time DESC;
檢索每個位置的最新天氣報告。但是,如果我們未使用 ORDER BY
強制每個位置的時間值降序排列,我們將獲得每個位置不可預測的時間的報告。
DISTINCT ON
表達式必須與最左側的 ORDER BY
表達式相符。ORDER BY
子句通常會包含其他表達式,這些表達式確定每個 DISTINCT ON
群組中列的所需優先順序。
目前,無法將 FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
與 DISTINCT
一起指定。
UNION
子句UNION
子句具有以下一般形式
select_statement
UNION [ ALL | DISTINCT ]select_statement
select_statement
是任何沒有 ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或 FOR KEY SHARE
子句的 SELECT
語句。(如果子表達式用括號括起來,則可以将 ORDER BY
和 LIMIT
附加到該子表達式。如果沒有括號,則這些子句將被視為應用於 UNION
的結果,而不是其右手輸入表達式。)
UNION
運算子計算所涉及的 SELECT
語句返回的列的集合聯集。如果列出現在至少一個結果集中,則該列位於兩個結果集的集合聯集中。代表 UNION
直接運算元的兩個 SELECT
語句必須產生相同數量的欄位,並且相應的欄位必須是相容的資料類型。
UNION
的結果不包含任何重複列,除非指定了 ALL
選項。ALL
可防止刪除重複項。(因此,UNION ALL
通常比 UNION
快得多;請在可以時使用 ALL
。)DISTINCT
可以被撰寫來明確指定刪除重複列的預設行為。
同一個 SELECT
語句中的多個 UNION
運算子從左到右評估,除非括號另有說明。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能為 UNION
的結果或 UNION
的任何輸入指定。
INTERSECT
子句INTERSECT
子句具有以下一般形式
select_statement
INTERSECT [ ALL | DISTINCT ]select_statement
select_statement
是任何沒有 ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或 FOR KEY SHARE
子句的 SELECT
語句。
INTERSECT
運算子計算所涉及的 SELECT
語句返回的列的集合交集。如果列出現在兩個結果集中,則該列位於兩個結果集的交集中。
除非指定 ALL
選項,否則 INTERSECT
的結果不會包含任何重複的列。 使用 ALL
時,如果某列在左側表格中有 m
個重複項,在右側表格中有 n
個重複項,則該列在結果集中將出現 min(m
,n
) 次。 可以寫入 DISTINCT
來明確指定消除重複列的預設行為。
在同一個 SELECT
語句中,多個 INTERSECT
運算子會從左到右進行評估,除非使用括號另行指定。 INTERSECT
的優先順序高於 UNION
。 也就是說,A UNION B INTERSECT C
會被解讀為 A UNION (B INTERSECT C)
。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能為 INTERSECT
的結果或 INTERSECT
的任何輸入指定。
EXCEPT
子句EXCEPT
子句具有以下一般形式
select_statement
EXCEPT [ ALL | DISTINCT ]select_statement
select_statement
是任何沒有 ORDER BY
、LIMIT
、FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
或 FOR KEY SHARE
子句的 SELECT
語句。
EXCEPT
運算子會計算出存在於左側 SELECT
語句的結果中,但不存在於右側語句結果中的列集合。
除非指定 ALL
選項,否則 EXCEPT
的結果不會包含任何重複的列。 使用 ALL
時,如果某列在左側表格中有 m
個重複項,在右側表格中有 n
個重複項,則該列在結果集中將出現 max(m
-n
,0) 次。 可以寫入 DISTINCT
來明確指定消除重複列的預設行為。
在同一個 SELECT
語句中,多個 EXCEPT
運算子會從左到右進行評估,除非使用括號另行指定。 EXCEPT
的優先順序與 UNION
相同。
目前,FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
和 FOR KEY SHARE
不能為 EXCEPT
的結果或 EXCEPT
的任何輸入指定。
ORDER BY
子句可選的 ORDER BY
子句具有以下一般形式
ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...]
ORDER BY
子句會使結果列根據指定的表達式進行排序。 如果根據最左邊的表達式,兩列相等,則根據下一個表達式進行比較,依此類推。 如果它們根據所有指定的表達式都相等,則它們會以與實現相關的順序傳回。
每個 expression
可以是輸出列(SELECT
列表項目)的名稱或序號,也可以是由輸入列值組成的任意表達式。
序號指的是輸出列的序數(從左到右)位置。 此功能使您可以根據沒有唯一名稱的列來定義排序。 這絕不是絕對必要的,因為始終可以使用 AS
子句將名稱分配給輸出列。
也可以在 ORDER BY
子句中使用任意表達式,包括未出現在 SELECT
輸出列表中的列。 因此,以下語句是有效的
SELECT name FROM distributors ORDER BY code;
此功能的一個限制是,應用於 UNION
、INTERSECT
或 EXCEPT
子句結果的 ORDER BY
子句只能指定輸出列名稱或數字,而不能指定表達式。
如果 ORDER BY
表達式是一個簡單的名稱,它同時匹配輸出列名稱和輸入列名稱,則 ORDER BY
會將其解釋為輸出列名稱。 這與 GROUP BY
在相同情況下所做的選擇相反。 這種不一致是為了與 SQL 標準相容。
您可以選擇在 ORDER BY
子句中的任何表達式之後添加關鍵字 ASC
(升序) 或 DESC
(降序)。 如果未指定,則預設情況下會假定 ASC
。 或者,可以在 USING
子句中指定特定的排序運算子名稱。 排序運算子必須是某些 B 樹運算子家族的小於或大於成員。 ASC
通常等效於 USING <
,而 DESC
通常等效於 USING >
。(但是,使用者定義資料類型的建立者可以精確定義預設排序順序,並且它可能對應於具有其他名稱的運算子。)
如果指定了 NULLS LAST
,則 null 值會排在所有非 null 值之後;如果指定了 NULLS FIRST
,則 null 值會排在所有非 null 值之前。 如果兩者都未指定,則預設行為是在指定或暗示 ASC
時為 NULLS LAST
,而在指定 DESC
時為 NULLS FIRST
(因此,預設行為就像 null 值大於非 null 值)。 指定 USING
時,預設的 null 值排序取決於運算子是小於運算子還是大於運算子。
請注意,排序選項僅適用於它們後面的表達式;例如,ORDER BY x, y DESC
與 ORDER BY x DESC, y DESC
的含義不同。
字串資料根據應用於被排序的列的定序進行排序。 可以在需要時通過在 expression
中包含 COLLATE
子句來覆寫該定序,例如 ORDER BY mycolumn COLLATE "en_US"
。 有關更多資訊,請參閱 第 4.2.10 節 和 第 23.2 節。
LIMIT
子句LIMIT
子句由兩個獨立的子子句組成
LIMIT {count
| ALL } OFFSETstart
參數 count
指定要傳回的最大列數,而 start
指定在開始傳回列之前要跳過的列數。 當同時指定兩者時,會在開始計算要傳回的 count
列之前跳過 start
列。
如果 count
表達式的計算結果為 NULL,則會將其視為 LIMIT ALL
,即沒有限制。 如果 start
的計算結果為 NULL,則會將其視為與 OFFSET 0
相同。
SQL:2008 引入了一種不同的語法來實現相同的結果,PostgreSQL 也支援此語法。 它是
OFFSETstart
{ ROW | ROWS } FETCH { FIRST | NEXT } [count
] { ROW | ROWS } { ONLY | WITH TIES }
在這個語法中,標準要求 start
或 count
的值必須是字面常數、參數或變數名稱;作為 PostgreSQL 的擴充功能,允許使用其他表達式,但通常需要用括號括起來以避免歧義。 如果在 FETCH
子句中省略 count
,則預設為 1。 WITH TIES
選項用於根據 ORDER BY
子句傳回結果集中與最後一名並列的任何其他列;在這種情況下,ORDER BY
是強制性的,並且不允許使用 SKIP LOCKED
。 ROW
和 ROWS
以及 FIRST
和 NEXT
是噪音詞,不會影響這些子句的效果。 根據標準,如果同時存在 OFFSET
子句和 FETCH
子句,則 OFFSET
子句必須位於 FETCH
子句之前;但是 PostgreSQL 比較寬鬆,允許任何一種順序。
使用 LIMIT
時,最好使用 ORDER BY
子句將結果列限制為唯一順序。 否則,您將獲得查詢列的不可預測的子集 — 您可能要求第十到第二十列,但在什麼順序中是第十到第二十列? 除非您指定 ORDER BY
,否則您不知道什麼順序。
查詢規劃器在產生查詢計劃時會考慮 LIMIT
,因此您很可能會獲得不同的計劃(產生不同的列順序),具體取決於您對 LIMIT
和 OFFSET
的使用方式。 因此,除非您使用 ORDER BY
強制執行可預測的結果順序,否則使用不同的 LIMIT
/OFFSET
值來選擇查詢結果的不同子集將產生不一致的結果。 這不是一個錯誤;這是 SQL 不保證以任何特定順序傳遞查詢結果的固有結果,除非使用 ORDER BY
來限制順序。
如果沒有 ORDER BY
來強制選擇確定性的子集,即使是重複執行相同的 LIMIT
查詢,也可能會傳回表格列的不同子集。 同樣,這不是一個錯誤;在這種情況下,結果的確定性根本無法保證。
FOR UPDATE
、FOR NO KEY UPDATE
、FOR SHARE
和 FOR KEY SHARE
是鎖定子句;它們會影響 SELECT
如何在從表格中獲取列時鎖定這些列。
鎖定子句具有以下一般形式
FORlock_strength
[ OFfrom_reference
[, ...] ] [ NOWAIT | SKIP LOCKED ]
其中 lock_strength
可以是以下之一
UPDATE NO KEY UPDATE SHARE KEY SHARE
from_reference
必須是表格 alias
或在 FROM
子句中引用的非隱藏 table_name
。 有關每個列級別鎖定模式的更多資訊,請參閱 第 13.3.2 節。
為了防止操作等待其他事務提交,請使用 NOWAIT
或 SKIP LOCKED
選項。 使用 NOWAIT
,如果無法立即鎖定選定的列,則語句會報告錯誤,而不是等待。 使用 SKIP LOCKED
,將跳過任何無法立即鎖定的選定列。 跳過鎖定的列會提供不一致的資料檢視,因此不適合一般用途,但可用於避免多個使用者存取類似佇列的表格時的鎖定爭用。 請注意,NOWAIT
和 SKIP LOCKED
僅適用於列級別鎖定 — 所需的 ROW SHARE
表格級別鎖定仍然以普通方式取得(請參閱第 13 章)。 如果您需要先取得表格級別鎖定而不等待,則可以首先將 LOCK
與 NOWAIT
選項一起使用。
如果在鎖定子句中指定了特定表格,則僅鎖定來自這些表格的列;SELECT
中使用的任何其他表格都像平常一樣讀取。 沒有表格清單的鎖定子句會影響語句中使用的所有表格。 如果鎖定子句應用於檢視或子查詢,則它會影響檢視或子查詢中使用的所有表格。 但是,這些子句不適用於主要查詢引用的 WITH
查詢。 如果您希望在 WITH
查詢中發生列鎖定,請在 WITH
查詢中指定鎖定子句。
如果需要為不同的表格指定不同的鎖定行為,則可以編寫多個鎖定子句。 如果多個鎖定子句提及(或隱式影響)同一表格,則會將其視為僅由最強的鎖定子句指定來處理。 類似地,如果在影響表格的任何子句中指定了 NOWAIT
,則該表格將被視為 NOWAIT
來處理。 否則,如果在影響表格的任何子句中指定了 SKIP LOCKED
,則該表格將被視為 SKIP LOCKED
來處理。
鎖定子句不能用於無法將傳回的列與單個表格列清楚地識別的上下文中;例如,它們不能與聚合一起使用。
當鎖定子句出現在 SELECT
查詢的頂層時,鎖定的列正是查詢傳回的列;在連接查詢的情況下,鎖定的列是構成傳回的連接列的列。 此外,將鎖定在查詢快照時滿足查詢條件的列,即使它們在快照後已更新並且不再滿足查詢條件,也不會傳回。 如果使用 LIMIT
,則在傳回足夠的列以滿足限制後,鎖定會停止(但請注意,OFFSET
跳過的列將被鎖定)。 類似地,如果在游標的查詢中使用鎖定子句,則僅會鎖定游標實際提取或步進通過的列。
當鎖定子句出現在子 SELECT
中時,鎖定的列是子查詢傳回給外部查詢的列。 這可能涉及比單獨檢查子查詢所建議的更少的列,因為外部查詢的條件可用於最佳化子查詢的執行。 例如,
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;
將僅鎖定具有 col1 = 5
的列,即使該條件在文字上不在子查詢中。
先前的版本未能保留由後面的儲存點升級的鎖定。 例如,此程式碼
BEGIN; SELECT * FROM mytable WHERE key = 1 FOR UPDATE; SAVEPOINT s; UPDATE mytable SET ... WHERE key = 1; ROLLBACK TO s;
會在 ROLLBACK TO
之後無法保留 FOR UPDATE
鎖定。 這已在版本 9.3 中修復。
在 READ COMMITTED
交易隔離層級下運行的 SELECT
命令,並且使用了 ORDER BY
和鎖定子句,可能會傳回順序錯亂的資料列。這是因為 ORDER BY
會先被執行。該命令會先將結果排序,但之後可能會因為嘗試取得一或多個資料列的鎖定而阻塞。一旦 SELECT
解除阻塞,某些排序欄位的值可能已被修改,導致這些資料列看起來順序錯亂 (雖然它們根據原始欄位值仍然是有序的)。如果需要,可以將 FOR UPDATE/SHARE
子句放在子查詢中來解決這個問題,例如:
SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
請注意,這會導致鎖定 mytable
的所有資料列,而頂層的 FOR UPDATE
只會鎖定實際傳回的資料列。這可能會造成顯著的效能差異,特別是當 ORDER BY
與 LIMIT
或其他限制結合使用時。因此,只有在預期並行更新排序欄位並且需要嚴格排序的結果時,才建議使用此技術。
在 REPEATABLE READ
或 SERIALIZABLE
交易隔離層級下,這會導致序列化失敗 (具有 SQLSTATE
的 '40001'
),因此在這些隔離層級下不可能收到順序錯亂的資料列。
TABLE
命令此命令
TABLE name
等同於
SELECT * FROM name
它可以用作頂層命令,也可以用作複雜查詢中節省空間的語法變體。只有 WITH
、UNION
、INTERSECT
、EXCEPT
、ORDER BY
、LIMIT
、OFFSET
、FETCH
和 FOR
鎖定子句可以與 TABLE
一起使用;WHERE
子句和任何形式的聚合都不能使用。
要將 films
表格與 distributors
表格連接起來
SELECT f.title, f.did, d.name, f.date_prod, f.kind FROM distributors d JOIN films f USING (did); title | did | name | date_prod | kind -------------------+-----+--------------+------------+---------- The Third Man | 101 | British Lion | 1949-12-23 | Drama The African Queen | 101 | British Lion | 1951-08-11 | Romantic ...
要對所有電影的 len
欄位求和,並按 kind
欄位對結果進行分組
SELECT kind, sum(len) AS total FROM films GROUP BY kind; kind | total ----------+------- Action | 07:34 Comedy | 02:58 Drama | 14:28 Musical | 06:42 Romantic | 04:38
要對所有電影的 len
欄位求和,按 kind
欄位對結果進行分組,並顯示總計小於 5 小時的群組
SELECT kind, sum(len) AS total FROM films GROUP BY kind HAVING sum(len) < interval '5 hours'; kind | total ----------+------- Comedy | 02:58 Romantic | 04:38
以下兩個範例是以相同的方式根據第二個欄位 (name
) 的內容對個別結果進行排序
SELECT * FROM distributors ORDER BY name; SELECT * FROM distributors ORDER BY 2; did | name -----+------------------ 109 | 20th Century Fox 110 | Bavaria Atelier 101 | British Lion 107 | Columbia 102 | Jean Luc Godard 113 | Luso films 104 | Mosfilm 103 | Paramount 106 | Toho 105 | United Artists 111 | Walt Disney 112 | Warner Bros. 108 | Westward
下一個範例顯示如何取得 distributors
表格和 actors
表格的聯集,並將結果限制為每個表格中以字母 W 開頭的結果。只需要不同的資料列,因此省略了關鍵字 ALL
。
distributors: actors: did | name id | name -----+-------------- ----+---------------- 108 | Westward 1 | Woody Allen 111 | Walt Disney 2 | Warren Beatty 112 | Warner Bros. 3 | Walter Matthau ... ... SELECT distributors.name FROM distributors WHERE distributors.name LIKE 'W%' UNION SELECT actors.name FROM actors WHERE actors.name LIKE 'W%'; name ---------------- Walt Disney Walter Matthau Warner Bros. Warren Beatty Westward Woody Allen
此範例顯示如何在 FROM
子句中使用函式,無論是否帶有欄位定義清單
CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE SQL; SELECT * FROM distributors(111); did | name -----+------------- 111 | Walt Disney CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$ SELECT * FROM distributors WHERE did = $1; $$ LANGUAGE SQL; SELECT * FROM distributors_2(111) AS (f1 int, f2 text); f1 | f2 -----+------------- 111 | Walt Disney
這是一個添加了序數欄位的函式範例
SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY; unnest | ordinality --------+---------- a | 1 b | 2 c | 3 d | 4 e | 5 f | 6 (6 rows)
此範例顯示如何使用簡單的 WITH
子句
WITH t AS ( SELECT random() as x FROM generate_series(1, 3) ) SELECT * FROM t UNION ALL SELECT * FROM t; x -------------------- 0.534150459803641 0.520092216785997 0.0735620250925422 0.534150459803641 0.520092216785997 0.0735620250925422
請注意,WITH
查詢只被評估一次,因此我們獲得了兩組相同的三個隨機值。
此範例使用 WITH RECURSIVE
從只顯示直接下屬的表格中,找到員工 Mary 的所有下屬(直接或間接)及其間接層級
WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS ( SELECT 1, employee_name, manager_name FROM employee WHERE manager_name = 'Mary' UNION ALL SELECT er.distance + 1, e.employee_name, e.manager_name FROM employee_recursive er, employee e WHERE er.employee_name = e.manager_name ) SELECT distance, employee_name FROM employee_recursive;
請注意遞迴查詢的典型形式:一個初始條件,後跟 UNION
,後跟查詢的遞迴部分。請確保查詢的遞迴部分最終不會傳回任何元組,否則查詢將無限迴圈。(有關更多範例,請參閱第 7.8 節。)
此範例使用 LATERAL
對 manufacturers
表格的每一列應用傳回集合的函式 get_product_names()
SELECT m.name AS mname, pname FROM manufacturers m, LATERAL get_product_names(m.id) pname;
目前沒有任何產品的製造商將不會出現在結果中,因為它是內部連接。如果我們希望在結果中包含這些製造商的名稱,我們可以這樣做
SELECT m.name AS mname, pname FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;
當然,SELECT
語句與 SQL 標準相容。但存在一些擴充功能和一些缺失的功能。
FROM
子句PostgreSQL 允許省略 FROM
子句。它有一個直接的用途來計算簡單表達式的結果
SELECT 2+2; ?column? ---------- 4
其他一些SQL資料庫無法做到這一點,除非引入一個虛擬的單列表格來執行 SELECT
。
SELECT
清單SELECT
後面的輸出表達式清單可以為空,產生一個零欄位的結果表格。根據 SQL 標準,這不是有效的語法。PostgreSQL 允許這樣做,以保持與允許零欄位表格的一致性。但是,當使用 DISTINCT
時,不允許使用空清單。
AS
關鍵字在 SQL 標準中,當新的欄位名稱是有效的欄位名稱(即與任何保留關鍵字不同)時,可以在輸出欄位名稱之前省略可選的關鍵字 AS
。PostgreSQL 稍微更嚴格:如果新的欄位名稱與任何關鍵字匹配,無論是否保留,則需要 AS
。建議的做法是使用 AS
或雙引號括住輸出欄位名稱,以防止與未來添加的關鍵字發生任何可能的衝突。
在 FROM
項目中,標準和 PostgreSQL 都允許在別名是未保留的關鍵字之前省略 AS
。但是,由於語法上的歧義,這對於輸出欄位名稱來說是不切實際的。
FROM
中的子 SELECT
別名根據 SQL 標準,FROM
清單中的子 SELECT
必須具有別名。在 PostgreSQL 中,可以省略此別名。
ONLY
和繼承SQL 標準要求在撰寫 ONLY
時,表格名稱周圍使用括號,例如 SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
。PostgreSQL 認為這些括號是可選的。
PostgreSQL 允許撰寫尾隨的 *
,以明確指定包含子表格的非 ONLY
行為。標準不允許這樣做。
(這些要點同樣適用於所有支援 ONLY
選項的 SQL 命令。)
TABLESAMPLE
子句限制目前僅在常規表格和具體化檢視表上接受 TABLESAMPLE
子句。根據 SQL 標準,應該可以將其應用於任何 FROM
項目。
FROM
中的函式呼叫PostgreSQL 允許將函式呼叫直接寫成 FROM
清單的成員。在 SQL 標準中,需要將這樣的函式呼叫包裝在子 SELECT
中;也就是說,語法 FROM
大致等同於 func
(...) alias
FROM LATERAL (SELECT
。請注意,func
(...)) alias
LATERAL
被認為是隱含的;這是因為標準要求 FROM
中的 UNNEST()
項目使用 LATERAL
語意。PostgreSQL 以與其他傳回集合的函式相同的方式處理 UNNEST()
。
GROUP BY
和 ORDER BY
可用的命名空間在 SQL-92 標準中,ORDER BY
子句只能使用輸出欄位的名稱或編號,而 GROUP BY
子句只能使用基於輸入欄位名稱的表達式。PostgreSQL 擴展了這兩個子句,也允許另一種選擇(但如果存在歧義,它會使用標準的解釋)。PostgreSQL 還允許這兩個子句指定任意表達式。請注意,表達式中出現的名稱始終被視為輸入欄位名稱,而不是輸出欄位名稱。
SQL:1999 及之後的版本使用略有不同的定義,與 SQL-92 並非完全向上兼容。但在大多數情況下,PostgreSQL 對於 ORDER BY
或 GROUP BY
表達式的解釋方式與 SQL:1999 相同。
只有當資料表的主鍵包含在 GROUP BY
清單中時,PostgreSQL 才會識別函數依賴(允許從 GROUP BY
中省略欄位)。SQL 標準指定了應該識別的其他條件。
LIMIT
和 OFFSET
LIMIT
和 OFFSET
子句是 PostgreSQL 特有的語法,也被 MySQL 使用。SQL:2008 標準引入了 OFFSET ... FETCH {FIRST|NEXT} ...
子句來實現相同的功能,如上文 LIMIT 子句 中所示。這種語法也被 IBM DB2 使用。(為 Oracle 編寫的應用程式經常使用一種變通方法,涉及自動生成的 rownum
欄位,該欄位在 PostgreSQL 中不可用,以實現這些子句的效果。)
FOR NO KEY UPDATE
、FOR UPDATE
、FOR SHARE
、FOR KEY SHARE
儘管 FOR UPDATE
出現在 SQL 標準中,但標準只允許它作為 DECLARE CURSOR
的一個選項。PostgreSQL 允許它在任何 SELECT
查詢以及子查詢中,但這是一種擴展。FOR NO KEY UPDATE
、FOR SHARE
和 FOR KEY SHARE
變體,以及 NOWAIT
和 SKIP LOCKED
選項,沒有出現在標準中。
WITH
子句中的資料修改語句PostgreSQL 允許將 INSERT
、UPDATE
、DELETE
和 MERGE
用作 WITH
查詢。這在 SQL 標準中找不到。
DISTINCT ON ( ... )
是 SQL 標準的擴展。
ROWS FROM( ... )
是 SQL 標準的擴展。
WITH
的 MATERIALIZED
和 NOT MATERIALIZED
選項是 SQL 標準的擴展。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步說明的地方,請使用此表單來回報文件問題。