本文中含有需要您注意的重要提示信息,忽略該信息可能對您的業務造成影響,請務必仔細閱讀。
本文介紹了如何通過SELECT
語句從表或視圖檢索行。
語法
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
[ * | expression [ [ AS ] output_name ] [, ...] ]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY grouping_element [, ...] ]
[ HAVING condition ]
[ WINDOW window_name AS ( window_definition ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start [ ROW | ROWS ] ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
[ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]
其中 from_item 可以是以下之一:
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ TABLESAMPLE sampling_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 [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]
并且 grouping_element 可以是以下之一:
( )
expression
( expression [, ...] )
ROLLUP ( { expression | ( expression [, ...] ) } [, ...] )
CUBE ( { expression | ( expression [, ...] ) } [, ...] )
GROUPING SETS ( grouping_element [, ...] )
并且 with_query 是:
with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] ( select | values | insert | update | delete )
TABLE [ ONLY ] table_name [ * ]
說明
SELECT
語句支持在單個或多個表中檢索目標行。 SELECT
的通常處理如下:
WITH
列表中的所有查詢都會被計算。這些查詢實際充當了在FROM
列表中可以引用的臨時表。在FROM
中被引用多次的WITH
查詢只會被計算一次,除非另有說明,否則NOT MATERIALIZED
。FROM
列表中的所有元素都會被計算(FROM
中的每一個元素都是一個真實表或者虛擬表)。 如果在FROM
列表中指定了多于一個元素,它們會被交叉連接在一起。如果指定了
WHERE
子句,所有不滿足該條件的行都會被從輸出中消除。如果指定了
GROUP BY
子句或者如果有聚集函數,輸出會被組合成由在一個或者多個值上匹配的行構成的分組,并且在其上計算聚集函數的結果。如果出現了HAVING
子句,它會消除不滿足給定條件的分組。對于每一個被選中的行或者行組,會使用
SELECT
輸出表達式計算實際的輸出行。SELECT DISTINCT
從結果中消除重復的行。SELECT DISTINCT ON
消除在所有指定表達式上匹配的行。SELECT ALL
(默認)將返回所有候選行, 包括重復的行。通過使用操作符
UNION
、INTERSECT
和EXCEPT
,多于一個SELECT
語句的輸出可以被整合形成結果集。UNION
操作符返回位于一個或者兩個結果集中的全部行。INTERSECT
操作符返回同時位于兩個結果集中的所有行。EXCEPT
操作符返回位于第一個結果集但不在第二個結果集中的行。在所有三種情況下, 重復行都會被消除(除非指定ALL
)。可以增加噪聲詞DISTINCT
來顯式地消除重復行。注意雖然ALL
是SELECT
自身的默認行為, 但這里DISTINCT
是默認行為。如果指定了
ORDER BY
子句,被返回的行會以指定的順序排序。如果沒有給定ORDER BY
,系統會以能最快產生行的順序返回它們。如果指定了
LIMIT
(或FETCH FIRST
) 或者OFFSET
子句,SELECT
語句只返回結果行的一個子集。如果指定了
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
語句。
在WITH
中寫一個數據修改語句(INSERT
、 UPDATE
或者 DELETE
)時,通常要包括一個 RETURNING
子句。構成被主查詢讀取的臨時表的是 RETURNING
的輸出,而不是該語句修改的底層表。如果省略RETURNING
,該語句仍會被執行,但是它不會產生輸出,因此它不能作為一個表從主查詢引用。
對于每一個WITH
查詢,都必須指定一個名稱(無需模式限定)。可選的,可以指定一個列名列表。如果省略該列表,會從該子查詢中推導列名。
如果指定了RECURSIVE
,則允許 SELECT
子查詢使用名稱引用自身。 這樣子查詢的形式必須為:
non_recursive_term UNION [ ALL | DISTINCT ] recursive_term
其中遞歸自引用必須出現在UNION
的右手邊。每個查詢中只允許一個遞歸自引用。不支持遞歸數據修改語句,但支持在數據查詢語句中使用遞歸SELECT
查詢的結果。
RECURSIVE
的另一個效果是 WITH
查詢不需要被排序:一個查詢可以引用另一個在列表中比它靠后的查詢(循環引用或者互遞歸沒有實現)。 如果沒有RECURSIVE
,WITH
查詢只能引用在WITH
列表中位置更前面的兄弟 WITH
查詢。
當WITH
子句中有多個查詢時,RECURSIVE
應只編寫一次,緊跟在WITH
之后。 它適用于WITH
子句中的所有查詢,盡管它對不使用遞歸或前向引用的查詢沒有影響。
主查詢以及WITH
查詢全部(理論上)在同一時間被執行。這意味著從該查詢的任何部分都無法看到 WITH
中的一個數據修改語句的效果,不過可以讀取其RETURNING
輸出。如果兩個這樣的數據修改語句嘗試修改相同的行,結果將無法確定。
WITH
查詢的一個關鍵屬性是,即使主查詢多次引用它們,它們通常每次執行主查詢只計算一次。 特別是,數據修改語句確保執行一次而且只執行一次,而與主查詢是否讀取它們的全部或任何輸出無關。
但是,WITH
查詢可以標記為NOT MATERIALIZED
以移除此保證。 在這種情況下,WITH
查詢可以折疊到主查詢中,就好像它是主查詢的FROM
子句中的簡單的 sub-SELECT
。 如果主查詢多次引用WITH
查詢,則會導致重復計算。但是,如果每次此類使用只需要WITH
查詢的總輸出中的幾行,NOT MATERIALIZED
可以通過允許查詢聯合優化來節省開銷。 NOT MATERIALIZED
被忽略,如果它被附加到一個遞歸的WITH
查詢,或者不是邊際效應無關的(也就是說,不是包含非易失性函數的普通的SELECT
)。
默認情況下,如果查詢在主查詢的FROM
子句中僅一次使用,則邊際效應無關的WITH
查詢將折疊到主查詢中。 這允許在語義不可見的情況下兩個查詢級別的聯合優化。 但是,通過將WITH
查詢標記為MATERIALIZED
,可以防止此類折疊。 這可能很有用,例如,如果WITH
查詢被用作優化圍欄,以防止規劃者選擇錯誤計劃。
FROM 子句
FROM
子句為SELECT
指定一個或者更多源表。如果指定了多個源表,結果將是所有源表的笛卡爾積(交叉連接)。但是通常會增加限定條件(通過 WHERE
)來把返回的行限制為該笛卡爾積的一個小子集。
FROM
子句可以包含下列元素:
table_name
:一個現有表或視圖的名稱(可以是模式限定的)。如果在表名前指定了ONLY
,則只會掃描該表。如果沒有指定ONLY
,該表及其所有后代表(如果有)都會被掃描。可選的,可以在表名后指定來顯式地指示包括后代表。alias
:一個包含別名的FROM
項的替代名稱。別名被用于讓書寫簡潔或者消除自連接中的混淆(其中同一個表會被掃描多次)。當提供一個別名時,表或者函數的實際名稱會被隱藏。例如,給定FROM foo AS f
,SELECT
的剩余部分就必須以f
而不是foo
來引用這個FROM
項。如果寫了一個別名,還可以寫一個列別名列表來為該表的一個或者多個列提供替代名稱。TABLESAMPLE
sampling_method
(
argument
[, ...] ) [ REPEATABLE (
seed
) ]
:table_name
之后的TABLESAMPLE
子句表示應該用指定的sampling_method
來檢索表中行的子集。這種采樣優先于任何其他過濾器(例如WHERE
子句)。標準發布包括兩種采樣方法:BERNOULLI
和SYSTEM
, 其他采樣方法可以通過擴展安裝在數據庫中。BERNOULLI
以及SYSTEM
采樣方法都接受一個參數
,它表示要采樣的表的分數,表示為一個0到100之間的百分數。這個參數可以是任意的實數值
表達式(其他的采樣方法可能接受更多或者不同的參數)。這兩種方法都返回一個隨機選取的該表采樣,其中包含了指定百分數的表行。BERNOULLI
方法掃描整個表并且用指定的幾率選擇或者忽略行。SYSTEM
方法會做塊層的采樣,每個塊都有指定的機會能被選中,被選中塊中的所有行都會被返回。在指定較小的采樣百分數時,SYSTEM
方法要比BERNOULLI
方法快很多,但是前者可能由于聚簇效應返回隨機性較差的表采樣。可選的
REPEATABLE
子句指定一個用于產生采樣方法中隨機數的種子
數或表達式。種子值可以是任何非空浮點值。如果查詢時表沒有被更改,指定相同種子和argument
值的兩個查詢將會選擇該表相同的采樣。但是不同的種子值通常將會產生不同的采樣。如果沒有給出REPEATABLE
,則會基于一個系統產生的種子為每一個查詢選擇一個新的隨機采樣。注意有些擴展采樣方法不接受REPEATABLE
,并且將總是為每一次使用產生新的采樣。select
:一個子SELECT
可以出現在FROM
子句中。這就好像把它的輸出創建為一個存在于該SELECT
命令期間的臨時表。子-SELECT
必須用圓括號包圍,并且 必須為它提供一個別名。也可以在這里使用一個 VALUES 命令。with_query_name
:可以通過寫一個WITH
查詢的名稱來引用它,就好像該查詢的名稱是一個表名(實際上,該WITH
查詢會為主查詢隱藏任何具有相同名稱的真實表。如果必要,你可以使用帶模式限定的方式以相同的名稱來引用真實表)。可以像表一樣, 以同樣的方式提供別名。function_name
:函數調用可以出現在FROM
子句中(對于返回結果集合的函數特別有用,但是可以使用任何函數)。這就好像把該函數的輸出創建為一個存在于該SELECT
命令期間的臨時表。當為該函數調用增加可選的WITH ORDINALITY
子句時,會在該函數的輸出列之后追加一個新的列來為每一行編號。可以用和表一樣的方式提供一個別名。如果寫了一個別名,還可以寫一個列別名列表來為該函數的組合返回類型的一個或者多個屬性提供替代名稱, 包括由
ORDINALITY
(如果有)增加的新列。通過把多個函數調用包圍在
ROWS FROM( ... )
中可以把它們整合在單個FROM
-子句項中。這樣一個項的輸出是把每一個函數的第一行串接起來,然后是每個函數的第二行,以此類推。如果有些函數產生的行比其他函數少,則在缺失數據的地方放上空值,這樣被返回的總行數總是和產生最多行的函數一樣。如果函數被定義為返回
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
CROSS JOIN
對于
INNER
和OUTER
連接類型,必須指定一個連接條件,即NATURAL
、ON
join_condition
或者USING (
join_column
[, ...])
之一(只能有一種)。其含義見下文。對于CROSS JOIN
,上述子句不能出現。一個
JOIN
子句聯合兩個FROM
項( 為了方便我們稱之為“表”,盡管實際上它們可以是任何類型的FROM
項)。如有必要可以使用圓括號確定嵌套的順序。 在沒有圓括號時,JOIN
會從左至右嵌套。在任何情況下,JOIN
的聯合比分隔FROM
-列表項的逗號更強。CROSS JOIN
和INNER JOIN
會產生簡單的笛卡爾積,也就是與在FROM
的頂層列出兩個表得到的結果相同,但是要用連接條件(如果有)約束該結果。CROSS JOIN
與INNER JOIN ON (TRUE)
等效,也就是說條件不會移除任何行。這些連接類型只是一種記號上的方便,因為沒有什么是你用純粹的FROM
和WHERE
能做而它們不能做的。LEFT OUTER JOIN
返回被限制過的笛卡爾積中的所有行(即所有通過了其連接條件的組合行),外加左手表中沒有相應的通過了連接條件的右手行的每一行的拷貝。通過在右手列中插入空值,這種左手行會被擴展為連接表的完整行。注意在決定哪些行匹配時,只考慮JOIN
子句自身的條件。之后才應用外條件。相反,
RIGHT OUTER JOIN
返回所有連接行,外加每一個沒有匹配上的右手行(在左端用空值擴展)。這只是為了記號上的方便,因為你可以通過交換左右表把它轉換成一個LEFT OUTER JOIN
。FULL OUTER JOIN
返回所有連接行,外加每一個沒有匹配上的左手行(在右端用空值擴展),再外加每一個沒有匹配上的右手行(在左端用空值擴展)。ON
join_condition
:join_condition
是一個會得到boolean
類型值的表達式(類似于一個WHERE
子句),它說明一次連接中哪些行被認為相匹配。USING (
join_column
[, ...] )
:格式USING ( a, b, ... )
的子句是ON left_table.a = right_table.a AND left_table.b = right_table.b ...
的簡寫。還有,USING
表示每一對相等列中只有一個會被包括在連接輸出中。NATURAL
:NATURAL
是一個USING
列表的速記,該列表中提到兩個表中具有匹配名稱的所有的列。如果沒有公共列名,則NATURAL
等效于ON TRUE
。LATERAL
:LATERAL
關鍵詞可以放在一個子-SELECT
FROM
項前面。這允許該子SELECT
引用FROM
列表中在它之前的FROM
項的列(如果沒有LATERAL
,每一個子SELECT
會被獨立計算并且因此不能交叉引用任何其他的FROM
項)。LATERAL
也可以放在一個函數調用FROM
項前面,但是在這種情況下它只是一個噪聲詞,因為在任何情況下函數表達式都可以引用在它之前的FROM
項。LATERAL
項可以出現在FROM
列表頂層,或者一個JOIN
中。在后一種情況中,它也可以引用其作為右手端的JOIN
左手端上的任何項。當一個
FROM
項包含LATERAL
交叉引用時,計算會如此進行:對提供被交叉引用列的FROM
項的每一行或者提供那些列的多個FROM
項的每一個行集,使用該行或者行集的那些列值計算LATERAL
項。結果行會與計算得到它們的行進行通常的連接。對來自哪些列的源表的每一行或者行集都會重復這樣的步驟。列的源表必須以
INNER
或者LEFT
的方式連接到LATERAL
項,否則就沒有用于為LATERAL
項計算每一個行集的良定行集。盡管X
RIGHT JOIN LATERAL
Y
這樣的結構在語法上是合法的, 但實際上不允許用于在Y
中引用X
。
WHERE 子句
可選的WHERE
子句的形式如下;
WHERE condition
其中 condition
是任一計算得到布爾
類型結果的表達式。任何不滿足這個條件的行都會從輸出中被去除。如果用一行的實際值替換其中的變量引用后,該表達式返回真,則該行符合條件。
GROUP BY 子句
可選的GROUP BY
子句的形式如下:
GROUP BY grouping_element [, ...]
GROUP BY
將會把所有被選擇的行中共享相同分組表達式值的那些行壓縮成一個行。一個被用在 grouping_element
中的 expression
可以是輸入列名、輸出列 (SELECT
列表項)的名稱或序號或者由輸入列值構成的任意表達式。在出現歧義時,GROUP BY
名稱將被解釋為輸入列名而不是輸出列名。
如果任何GROUPING SETS
、ROLLUP
或者 CUBE
作為分組元素存在,則GROUP BY
子句整體上定義了數個獨立的 分組集
。其效果等效于在子查詢間構建一個UNION ALL
,子查詢帶有分組集作為它們的GROUP BY
子句。
聚集函數(如果使用)會在組成每一個分組的所有行上進行計算,從而為每一個分組產生一個單獨的值(如果有聚集函數但是沒有 GROUP BY
子句,則查詢會被當成是由所有選中行構成的一個單一分組)。傳遞給每一個聚集函數的行集合可以通過在聚集函數調用附加一個FILTER
子句來進一步過濾。當存在FILTER
子句時,只有那些匹配它的行才會被包括在該聚集函數的輸入中。
當存在GROUP BY
子句或者任何聚集函數時,SELECT
列表表達式不能引用非分組列(除非它出現在聚集函數中或者它函數依賴于分組列),因為這樣做會導致返回非分組列的值時會有多種可能的值。如果分組列是包含非分組列的表的主鍵( 或者主鍵的子集),則存在函數依賴。
記住所有的聚集函數都是在HAVING
子句或者 SELECT
列表中的任何“標量”表達式之前被計算。 這意味著一個CASE
表達式不能被用來跳過一個聚集表達式的計算。
當前,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
條件為真,這樣一個查詢將會發出一個單一行; 否則不返回行。
當前,FOR NO KEY UPDATE
、FOR UPDATE
、 FOR SHARE
和FOR KEY SHARE
不能與 HAVING
一起指定。
WINDOW 子句
可選的WINDOW
子句的形式如下:
WINDOW window_name AS ( window_definition ) [, ...]
其中 window_name
是一個可以從OVER
子句或者后續窗口定義中引用的名稱。 window_definition
如下:
[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]
如果指定了一個 existing_window_name
, 它必須引用WINDOW
列表中一個更早出現的項。新窗口將從該項中復制它的劃分子句以及排序子句(如果有)。在這種情況下,新窗口不能指定它自己的PARTITION 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 } BETWEEN frame_start AND frame_end [ frame_exclusion ]
之一,其中 frame_start
和 frame_end
可以是:
UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset 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
的選擇在上面 of frame_start
以及 frame_end
選項的列表中不能早于 frame_start
的選擇 — 例如 RANGE BETWEEN CURRENT ROW AND
offset
PRECEDING
是不被允許的。
默認的幀選項是RANGE UNBOUNDED PRECEDING
,它和 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
相同。它把幀設置為從分區開始直到當前行的最后一個平級行(被該窗口的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
是一個整數,表示真開始或者結束于當前行的平級組之前或者之后那么多個平級組處,其中平級組是一組根據窗口的ORDER BY
子句等效的行。在RANGE
模式中, offset
選項的使用要求在窗口定義中正好有一個ORDER BY
列。那么該幀包含的行的排序列值不超過 offset
且小于(對于PRECEDING
)或者大于(對于FOLLOWING
)當前行的排序列值。在這些情況中, offset
表達式的數據類型取決于排序列的數據類型。對于數字排序列,它通常與排序列是相同類型,但對于 datetime 類型的排序列它是interval
。在所有這些情況中, offset
的值必須是非空和非負。此外,雖然 offset
并非必須是簡單常量,但它不能包含變量、聚集函數或者窗口函數。
frame_exclusion
選項允許從幀中排除當前行周圍的行,即便根據幀的起始選項來說它們應該被包含在幀中。EXCLUDE CURRENT ROW
把當前行從幀中排除。EXCLUDE GROUP
把當前行和它在排序上的平級行從幀中排除。EXCLUDE TIES
從幀中排除當前行的任何平級行,但是不排除當前行本身。EXCLUDE NO OTHERS
只是明確地指定不排除當前行或其平級行的默認行為。
注意,如果ORDER BY
排序無法把行唯一地排序,則ROWS
模式可能產生不可預測的結果。RANGE
以及GROUPS
模式的目的是確保在ORDER BY
順序中平等的行被同樣對待:一個給定平級組中的所有行將在一個幀中或者被從幀中排除。
WINDOW
子句的目的是指定出現在查詢的 SELECT list 或 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
,但只能在期望的輸出名稱不匹配任何 PolarDB 關鍵詞時省略。為了避免和未來增加的關鍵詞沖突, 推薦總是寫上AS
或者用雙引號引用輸出名稱)。如果你不指定列名, PolarDB會自動選擇一個名稱。如果列的表達式是一個簡單的列引用,那么被選擇的名稱就和該列的名稱相同。在使用函數或者類型名稱的更復雜的情況中,系統可能會生成諸如 ?column?
之類的名稱。
輸出列的名稱可以被用來在ORDER BY
以及 GROUP BY
子句中引用該列的值,但是不能用于 WHERE
和HAVING
子句(在其中必須寫出表達式)。
可以在輸出列表中寫來取代表達式,它是被選中行的所有列的一種簡寫方式。還可以寫 table_name
.
,它是指來自那個表的所有列的簡寫形式。在這些情況中無法用 AS
指定新的名稱,輸出行的名稱將和表列的名稱相同。
根據 SQL 標準,輸出列表中的表達式應該在應用DISTINCT
、ORDER BY
或者LIMIT
之前計算。在使用DISTINCT
時顯然必須這樣做,否則就無法搞清到底在區分什么值。不過,在很多情況下如果先計算ORDER BY
和LIMIT
再計算輸出表達式會很方便,特別是如果輸出列表中包含任何 volatile 函數或者代價昂貴的函數時尤其如此。通過這種行為,函數計算的順序更加直觀并且對于從未出現在輸出中的行將不會進行計算。只要輸出表達式沒有被DISTINCT
、ORDER BY
或者GROUP BY
引用,PolarDB實際將在排序和限制行數之后計算輸出表達式(作為一個反例,SELECT f(x) FROM tab ORDER BY 1
顯然必須在排序之前計算f(x)
)。包含有集合返回函數的輸出表達式實際是在排序之后和限制行數之前被計算,這樣LIMIT
才能切斷來自集合返回函數的輸出。
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
語句返回的行的交集。如果一行同時出現在兩個結果集中,它就在交集中。
INTERSECT
的結果不會包含重復行,除非指定了 ALL
選項。如果有ALL
,一個在左表中有 m
次重復并且在右表中有 n
次重復的行將會在結果中出現 min(m
,n
) 次。 DISTINCT
可以寫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
語句的結果中但不在右 SELECT
語句結果中的行集合。
EXCEPT
的結果不會包含重復行,除非指定了 ALL
選項。如果有ALL
,一個在左表中有 m
次重復并且在右表中有 n
次重復的行將會在結果集中出現 max(m
-n
,0) 次。 DISTINCT
可以寫DISTINCT
來顯式地指定消除重復行的行為。
除非用圓括號指定計算順序, 同一個SELECT
語句中的多個 EXCEPT
操作符會從左至右計算。 EXCEPT
的優先級與 UNION
相同。
當前,FOR NO KEY UPDATE
、FOR UPDATE
、 FOR SHARE
和 FOR KEY SHARE
不能用于EXCEPT
結果或者 EXCEPT
的任何輸出。
ORDER BY 子句
可選的ORDER BY
子句的形式如下:
ORDER BY expression [ ASC | DESC | USING operator ] [ 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
,空值會排在非空值之后;如果指定 NULLS FIRST
,空值會排在非空值之前。如果都沒有指定, 在指定或者隱含ASC
時的默認行為是NULLS LAST
, 而指定或者隱含DESC
時的默認行為是 NULLS FIRST
(因此,默認行為是空值大于非空值)。 當指定USING
時,默認的空值順序取決于該操作符是否為小于或者大于操作符。
注意順序選項只應用到它們所跟隨的表達式上。例如 ORDER BY x, y DESC
和 ORDER BY x DESC, y DESC
是不同的。
字符串數據會被根據引用到被排序列上的排序規則排序。根據需要可以通過在 expression
中包括一個 COLLATE
子句來覆蓋,例如 ORDER BY mycolumn COLLATE "en_US"
。
LIMIT 子句
LIMIT
子句由兩個獨立的子句構成:
LIMIT { count | ALL }
OFFSET start
參數 count
指定要返回的最大行數,而 start
指定在返回行之前要跳過的行數。在兩者都被指定時,在開始計算要返回的 count
行之前會跳過 start
行。
如果 count
表達式計算為NULL,它會被當成LIMIT ALL
,即沒有限制。如果 start
計算為 NULL,它會被當作OFFSET 0
。
SQL:2008 引入了一種不同的語法來達到相同的結果, PolarDB也支持它:
OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }
在這種語法中,標準要求 start
或 count
是一個文本常量、一個參數或者一個變量名。而作為一種 PolarDB的擴展,還允許其他的表達式,但通常需要被封閉在圓括號中以避免歧義。如果在一個 FETCH
子句中省略 count
,它的默認值為 1。 WITH TIES
選項用于根據ORDER BY
子句返回與結果集中最后一個位置相關的任何附加行; ORDER BY
在這種情況下是強制性的。 ROW
和ROWS
以及 FIRST
和NEXT
是噪聲,它們不影響這些子句的效果。根據標準,如果都存在,OFFSET
子句必須出現在FETCH
子句之前。但是 PolarDB更寬松,它允許兩種順序。
在使用LIMIT
時,用一個ORDER BY
子句把結果行約束到一個唯一順序是個好辦法。否則你講得到該查詢結果行的一個不可預測的子集 — 你可能要求從第10到第20行,但是在什么順序下的第10到第20,除非指定ORDER BY
,否則是不知道順序的。
查詢規劃器在生成一個查詢計劃時會考慮LIMIT
,因此根據你使用的LIMIT
和OFFSET
,你很可能得到不同的計劃(得到不同的行序)。所以,使用不同的 LIMIT
/OFFSET
值來選擇一個查詢結果的不同子集將會給出不一致的結果,除非用ORDER BY
強制一種可預測的結果順序。這不是一個缺陷,它是 SQL 不承諾以任何特定順序(除非使用 ORDER BY
來約束順序)給出一個查詢結果這一事實造成的必然后果。
如果沒有一個ORDER BY
來強制選擇一個確定的子集, 重復執行同樣的LIMIT
查詢甚至可能會返回一個表中行的不同子集。同樣,這也不是一種缺陷,在這樣一種情況下也無法保證結果的確定性。
鎖定子句
FOR UPDATE
、FOR NO KEY UPDATE
、 FOR SHARE
和FOR KEY SHARE
是鎖定子句,它們影響SELECT
把行從表中取得時如何對它們加鎖。
鎖定子句的一般形式:
FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ]
其中 lock_strength
可以是:
UPDATE
NO KEY UPDATE
SHARE
KEY SHARE
為了防止該操作等待其他事務提交,可使用NOWAIT
或者 SKIP LOCKED
選項。使用NOWAIT
時, 如果選中的行不能被立即鎖定,該語句會報告錯誤而不是等待。使用 SKIP LOCKED
時,無法被立即鎖定的任何選中行都會被跳過。跳過已鎖定行會提供數據的一個不一致的視圖,因此這不適合于一般目的的工作,但是可以被用來避免多個用戶訪問一個類似隊列的表時出現鎖競爭。注意NOWAIT
和 SKIP LOCKED
只適合行級鎖—所要求的 ROW SHARE
表級鎖仍然會以常規的方式取得。如果想要不等待的表級鎖,你可以先使用帶NOWAIT
的LOCK。
如果在一個鎖定子句中提到了特定的表,則只有來自于那些表的行會被鎖定,任何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
鎖。
運行在READ COMMITTED
事務隔離級別并且使用ORDER BY
和鎖定子句的SELECT
命令有可能返回無序的行。 這是因為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, films f
WHERE f.did = d.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
,然后是查詢的遞歸部分。要確保查詢的遞歸部分最終將不返回任何行,否則該查詢將無限循環。
這個例子使用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子句
PolarDB允許省略 FROM
子句。一種簡單的使用是計算簡單表達式的結果:
SELECT 2+2;
?column?
----------
4
某些其他 SQL 數據庫需要引入一個假的單行表放在該SELECT
的 FROM
子句中才能做到這一點。
如果沒有指定一個FROM
子句,該查詢就不能引用任何數據庫表。例如,下面的查詢是非法的:
SELECT distributors.* WHERE distributors.name = 'Westward';
空SELECT列表
SELECT
之后的輸出表達式列表可以為空, 這會產生一個零列的結果表。對SQL標準來說這不是合法的語法。PolarDB允許它是為了與允許零列表保持一致。不過在使用 DISTINCT
時不允許空列表。
省略AS關鍵詞
在SQL標準中,只要新列名是一個合法的列名(就是說與任何保留關鍵詞不同), 就可以省略輸出列名之前的可選關鍵詞AS
。 PolarDB要稍微嚴格些:只要新列名匹配任何關鍵詞(保留或者非保留)就需要AS
。推薦的習慣是使用 AS
或者帶雙引號的輸出列名來防止與未來增加的關鍵詞可能的沖突。
在FROM
項中,標準和PolarDB都允許省略非保留關鍵詞別名之前的AS
。但是由于語法的歧義,這無法用于輸出列名。
ONLY和繼承
在書寫ONLY
時,SQL標準要求在表名周圍加上圓括號,例如 SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...
。PolarDB認為這些圓括號是可選的。
PolarDB允許寫一個拖尾的來顯式指定包括子表的非-ONLY
行為。而標準則不允許這樣。
(這些點同等地適用于所有支持ONLY
選項的 SQL 命令)。
TABLESAMPLE子句限制
當前只在常規表和物化視圖上接受TABLESAMPLE
子句。 根據SQL標準,應該可以把它應用于任何FROM
項。
FROM中的函數調用
PolarDB允許一個函數調用被直接寫作 FROM
列表的一個成員。在SQL標準中,有必要把這樣一個函數調用包裹在一個子-SELECT
中。也就是說,語法 FROM
func
(...)
alias
近似等價于 FROM LATERAL (SELECT
func
(...))
alias
。 注意該LATERAL
被認為是隱式的,這是因為標準對于 FROM
中的一個UNNEST()
項要求 LATERAL
語義。PolarDB會把 UNNEST()
和其他集合返回函數同樣對待。
GROUP BY和ORDER BY可用的名字空間
在SQL-92標準中,一個ORDER BY
子句只能使用輸出列名或者序號,而一個GROUP BY
子句只能使用基于輸入列名的表達式。PolarDB擴展了這兩種子句以允許它們使用其他的選擇(但如果有歧義時還是使用標準的解釋)。PolarDB也允許兩種子句指定任意表達式。注意出現在一個表達式中的名稱將總是被當做輸入列名而不是輸出列名。
SQL:1999及其后的標準使用了一種略微不同的定義,它并不完全向后兼容SQL-92。不過,在大部分的情況下, PolarDB會以與 SQL:1999 相同的方式解釋ORDER BY
或GROUP BY
表達式。
函數依賴
只有當一個表的主鍵被包括在GROUP BY
列表中時, PolarDB才識別函數依賴(允許從GROUP BY
中省略列)。SQL 標準指定了應該要識別的額外情況。
FOR NO KEY UPDATE、FOR UPDATE、FOR SHARE、FOR KEY SHARE
盡管SQL標準中出現了FOR UPDATE
,但標準只允許它作為 DECLARE CURSOR
的一個選項。 PolarDB允許它出現在任何 SELECT
查詢以及子-SELECT
中,但這是一種擴展。FOR NO KEY UPDATE
、FOR SHARE
以及FOR KEY SHARE
變體以及NOWAIT
和SKIP LOCKED
選項沒有在標準中出現。
WITH中的數據修改語句
PolarDB允許將INSERT
、 UPDATE
以及DELETE
用作WITH
查詢。
非標準子句
DISTINCT ON ( ... )
是SQL標準的擴展。
ROWS FROM( ... )
是SQL標準的擴展。
WITH
的MATERIALIZED
和 NOT MATERIALIZED
選項是SQL標準的擴展。