值表示式用於多種上下文,例如 SELECT
命令的目標列表、INSERT
或 UPDATE
中的新列值,或多個命令中的搜尋條件。值表示式的結果有時稱為 標量,以區別於表表達式(即表)的結果。因此,值表示式也稱為 標量表達式(甚至簡稱為 表示式)。表示式語法允許使用算術、邏輯、集合和其他操作從基本部分計算值。
值表示式是以下之一:
除了這個列表之外,還有一些構造可以歸類為表示式,但不遵循任何通用語法規則。這些通常具有函式或運算子的語義,並在第 9 章的適當位置進行解釋。例如是 IS NULL
子句。
我們已經在第 4.1.2 節中討論了常量。以下各節將討論剩餘的選項。
列可以以下形式引用:
correlation
.columnname
相關名
是表的名稱(可能帶有模式名限定),或者是透過 FROM
子句定義的表的別名。當列名在當前查詢使用的所有表中唯一時,可以省略相關名和分隔點。(另請參見第 7 章。)
位置引數引用用於指示一個值,該值是從外部提供給 SQL 語句的。引數在 SQL 函式定義和準備查詢中使用。某些客戶端庫還支援將資料值與 SQL 命令字串分開指定,在這種情況下,引數用於引用與命令字串分離的資料值。引數引用的形式是:
$number
例如,考慮一個名為 dept
的函式的定義,如下所示:
CREATE FUNCTION dept(text) RETURNS dept AS $$ SELECT * FROM dept WHERE name = $1 $$ LANGUAGE SQL;
這裡 $1
指的是函式被呼叫時第一個函式引數的值。
如果一個表示式產生一個數組型別的值,那麼可以透過寫入以下內容來提取陣列值的特定元素:
expression
[subscript
]
或者提取多個相鄰元素(一個“陣列切片”)可以透過寫入以下內容:
expression
[lower_subscript
:upper_subscript
]
(此處,方括號 [ ]
表示字面量。)每個 下標
本身是一個表示式,它將被四捨五入為最接近的整數值。
通常,陣列 表示式
必須用括號括起來,但當要下標的表示式只是一個列引用或位置引數時,可以省略括號。此外,當原始陣列是多維的時,可以連線多個下標。例如:
mytable.arraycolumn[4] mytable.two_d_column[17][34] $1[10:42] (arrayfunction(a,b))[42]
最後一個示例中的括號是必需的。有關陣列的更多資訊,請參閱第 8.15 節。
如果一個表示式產生一個複合型別(行型別)的值,那麼可以透過寫入以下內容來提取行的特定欄位:
expression
.fieldname
通常,行 表示式
必須用括號括起來,但當要從中選擇的表示式只是一個表引用或位置引數時,可以省略括號。例如:
mytable.mycolumn $1.somecolumn (rowfunction(a,b)).col3
(因此,限定列引用實際上只是欄位選擇語法的特例。)提取複合型別表列欄位的一個重要特例是:
(compositecol).somefield (mytable.compositecol).somefield
此處括號是必需的,以表明 compositecol
是一個列名而不是表名,或者在第二種情況下 mytable
是一個表名而不是模式名。
您可以透過寫入 .*
來請求複合值的全部欄位:
(compositecol).*
此表示法根據上下文的不同表現不同;有關詳細資訊,請參閱第 8.16.5 節。
運算子呼叫的語法有兩種可能性:
表示式 運算子 表示式 (二元中綴運算子) |
運算子 表示式 (一元字首運算子) |
其中 運算子
標記遵循第 4.1.3 節的語法規則,或者是一個關鍵字 AND
、OR
和 NOT
,或者是一個限定運算子名稱,形式如下:
OPERATOR(
schema
.
operatorname
)
哪些運算子存在以及它們是單目還是雙目取決於系統或使用者定義了哪些運算子。第 9 章描述了內建運算子。
函式呼叫的語法是函式名(可能帶有模式名限定),後跟用括號括起來的引數列表:
function_name
([expression
[,expression
... ]] )
例如,以下計算 2 的平方根:
sqrt(2)
內建函式的列表在第 9 章中。使用者可以新增其他函式。
在發出查詢時,如果某些使用者不信任其他使用者,請在編寫函式呼叫時注意第 10.3 節中的安全注意事項。
引數可以選擇性地附加名稱。有關詳細資訊,請參閱第 4.3 節。
接受複合型別單個引數的函式可以選擇性地使用欄位選擇語法呼叫,反之亦然,欄位選擇也可以寫成函式樣式。也就是說,col(table)
和 table.col
的表示法是可互換的。此行為不是 SQL 標準,但 PostgreSQL 提供此功能,因為它允許使用函式來模擬“計算欄位”。有關更多資訊,請參閱第 8.16.5 節。
聚合表示式表示在查詢選擇的行上應用聚合函式。聚合函式將多個輸入減少為一個輸出值,例如輸入的總和或平均值。聚合表示式的語法是以下之一:
aggregate_name
(expression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
(ALLexpression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
(DISTINCTexpression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
( * ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
( [expression
[ , ... ] ] ) WITHIN GROUP (order_by_clause
) [ FILTER ( WHEREfilter_clause
) ]
其中 aggregate_name
是先前定義的聚合(可能帶有模式名限定),expression
是任何不包含聚合表示式或視窗函式呼叫的值表示式。可選的 order_by_clause
和 filter_clause
稍後描述。
聚合表示式的第一種形式對每個輸入行呼叫一次聚合。第二種形式與第一種相同,因為 ALL
是預設值。第三種形式對輸入行中找到的表示式的每個不同值(或多個表示式的集合)呼叫一次聚合。第四種形式對每個輸入行呼叫一次聚合;由於未指定特定輸入值,因此通常僅對 count(*)
聚合函式有用。最後一種形式用於 有序集聚合函式,稍後將進行描述。
大多數聚合函式會忽略 NULL 輸入,因此其中一個或多個表示式產生 NULL 的行將被丟棄。除非另有說明,否則可以假設所有內建聚合函式都是如此。
例如,count(*)
返回輸入行的總數;count(f1)
返回 f1
非 NULL 的輸入行的數量,因為 count
會忽略 NULL;而 count(distinct f1)
返回 f1
的不同非 NULL 值的數量。
通常,輸入行以未指定順序送入聚合函式。在許多情況下,這並不重要;例如,無論 min
接收輸入的順序如何,它都會產生相同的結果。但是,一些聚合函式(如 array_agg
和 string_agg
)產生的結果取決於輸入行的順序。使用此類聚合時,可選的 order_by_clause
可用於指定所需的順序。order_by_clause
具有與查詢級 ORDER BY
子句相同的語法,如第 7.5 節中所述,只是其表示式始終只是表示式,不能是輸出列名或數字。例如:
WITH vals (v) AS ( VALUES (1),(3),(4),(3),(2) ) SELECT array_agg(v ORDER BY v DESC) FROM vals; array_agg ------------- {4,3,3,2,1}
由於 jsonb
只保留最後一個匹配的鍵,因此其鍵的排序可能很重要:
WITH vals (k, v) AS ( VALUES ('key0','1'), ('key1','3'), ('key1','2') ) SELECT jsonb_object_agg(k, v ORDER BY v) FROM vals; jsonb_object_agg ---------------------------- {"key0": "1", "key1": "3"}
處理多引數聚合函式時,請注意 ORDER BY
子句位於所有聚合引數之後。例如,這樣寫:
SELECT string_agg(a, ',' ORDER BY a) FROM table;
而不是這樣:
SELECT string_agg(a ORDER BY a, ',') FROM table; -- incorrect
後者在語法上是有效的,但它表示一個帶兩個 ORDER BY
鍵的單引數聚合函式呼叫(第二個鍵因為是常量而相當無用)。
如果在 DISTINCT
中指定了 order_by_clause
,則 ORDER BY
表示式只能引用 DISTINCT
列表中的列。例如:
WITH vals (v) AS ( VALUES (1),(3),(4),(3),(2) ) SELECT array_agg(DISTINCT v ORDER BY v DESC) FROM vals; array_agg ----------- {4,3,2,1}
將 ORDER BY
放置在聚合的常規引數列表中(如目前所述),用於對通用和統計聚合的輸入行進行排序,對於這些聚合,排序是可選的。存在一類稱為 有序集聚合的聚合函式,對於這些函式 order_by_clause
是 必需的,通常是因為聚合的計算僅在特定輸入行順序的意義上才有意義。有序集聚合的典型示例包括排名和百分位數計算。對於有序集聚合,order_by_clause
寫入 WITHIN GROUP (...)
中,如上最終語法選項所示。 order_by_clause
中的表示式與常規聚合引數一樣,為每個輸入行計算一次,按照 order_by_clause
的要求排序,並作為輸入引數提供給聚合函式。(這與非 WITHIN GROUP
order_by_clause
的情況不同,後者不被視為聚合函式的引數。) WITHIN GROUP
之前的表示式(如果有)稱為 直接引數,以區別於 order_by_clause
中列出的 聚合引數。與常規聚合引數不同,直接引數僅為每個聚合呼叫計算一次,而不是為每個輸入行計算一次。這意味著它們只能包含變數,前提是這些變數由 GROUP BY
分組;此限制與直接引數不在聚合表示式內部時相同。直接引數通常用於每種聚合計算只作為一個有意義的值的情況,例如百分位數分數。直接引數列表可以為空;在這種情況下,只寫 ()
而不是 (*)
。(PostgreSQL 實際上會接受任一拼寫,但只有第一種符合 SQL 標準。)
SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households; percentile_cont ----------------- 50489
從 households
表中獲取 income
列的第 50 百分位數,即中位數。這裡 0.5
是一個直接引數;百分位數分數作為變化的行值是沒有意義的。
如果指定了 FILTER
,那麼只有 filter_clause
求值為 true 的輸入行才會被送入聚合函式;其他行將被丟棄。例如:
SELECT count(*) AS unfiltered, count(*) FILTER (WHERE i < 5) AS filtered FROM generate_series(1,10) AS s(i); unfiltered | filtered ------------+---------- 10 | 4 (1 row)
預定義的聚合函式在第 9.21 節中描述。使用者可以新增其他聚合函式。
聚合表示式只能出現在 SELECT
命令的結果列表或 HAVING
子句中。禁止在其他子句(如 WHERE
)中使用,因為這些子句在聚合結果形成之前在邏輯上進行了計算。
當聚合表示式出現在子查詢中時(請參閱第 4.2.11 節和第 9.24 節),聚合通常在子查詢的行上進行求值。但是,如果聚合的引數(以及可選的 filter_clause
)只包含外部級別變數,則會出現例外:聚合屬於最近的外部級別,並在該查詢的行上進行求值。然後,整個聚合表示式成為它出現的子查詢的外部引用,並在該子查詢的任何一次求值中充當常量。關於只能出現在結果列表或 HAVING
子句中的限制適用於聚合所屬的查詢級別。
視窗函式呼叫表示在查詢選擇的行的一部分上應用類似聚合的函式。與非視窗聚合呼叫不同,這不與將選定行分組為單個輸出行相關聯——每個行在查詢輸出中保持獨立。但是,視窗函式可以訪問屬於當前行組的所有行,根據視窗函式呼叫的分組規範(PARTITION BY
列表)。視窗函式呼叫的語法是以下之一:
function_name
([expression
[,expression
... ]]) [ FILTER ( WHEREfilter_clause
) ] OVERwindow_name
function_name
([expression
[,expression
... ]]) [ FILTER ( WHEREfilter_clause
) ] OVER (window_definition
)function_name
( * ) [ FILTER ( WHEREfilter_clause
) ] OVERwindow_name
function_name
( * ) [ FILTER ( WHEREfilter_clause
) ] OVER (window_definition
)
其中 window_definition
具有以下語法:
[existing_window_name
] [ PARTITION BYexpression
[, ...] ] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [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
此處,expression
代表任何不包含視窗函式呼叫的值表示式。
window_name
是對查詢 WINDOW
子句中定義的命名視窗規範的引用。或者,可以在括號內給出完整的 window_definition
,使用與在 WINDOW
子句中定義命名視窗相同的語法;有關詳細資訊,請參閱SELECT 參考頁。值得指出的是,OVER wname
與 OVER (wname ...)
並不完全等價;後者表示複製和修改視窗定義,如果引用的視窗規範包含框架子句,則會被拒絕。
PARTITION BY
子句將查詢的行分組到 分割槽中,視窗函式分別處理這些分割槽。PARTITION BY
的工作方式類似於查詢級的 GROUP BY
子句,只是它的表示式始終只是表示式,不能是輸出列名或數字。如果沒有 PARTITION BY
,則將查詢產生的所有行視為單個分割槽。ORDER BY
子句確定分割槽中行的處理順序。它的工作方式類似於查詢級的 ORDER BY
子句,但同樣不能使用輸出列名或數字。如果沒有 ORDER BY
,則行以未指定順序處理。
frame_clause
指定構成 視窗幀的行集,這是當前分割槽的一個子集,適用於作用於幀而不是整個分割槽的視窗函式。幀中的行集可能因當前行而異。幀可以以 RANGE
、ROWS
或 GROUPS
模式指定;在每種情況下,它從 frame_start
執行到 frame_end
。如果省略 frame_end
,則預設值為 CURRENT ROW
。
UNBOUNDED PRECEDING
的 frame_start
意味著幀從分割槽的第一個行開始,同樣,UNBOUNDED FOLLOWING
的 frame_end
意味著幀以分割槽的最後一個行結束。
在 RANGE
或 GROUPS
模式下,CURRENT ROW
的 frame_start
意味著幀從當前行的第一個 對等行(視窗 ORDER BY
子句將其排序為與當前行等效的行)開始,而 CURRENT ROW
的 frame_end
意味著幀以當前行的最後一個對等行結束。在 ROWS
模式下,CURRENT ROW
僅表示當前行。
在 offset
PRECEDING
和 offset
FOLLOWING
幀選項中,offset
必須是一個不包含任何變數、聚合函式或視窗函式的表示式。offset
的含義取決於幀模式:
在 ROWS
模式下,offset
必須產生一個非 NULL、非負整數,並且該選項意味著幀從當前行之前或之後指定的行數開始或結束。
在 GROUPS
模式下,offset
必須再次產生一個非 NULL、非負整數,並且該選項意味著幀在當前行的對等組之前或之後指定的 對等組數量開始或結束,其中對等組是與 ORDER BY
排序等效的行集。(要在 GROUPS
模式下使用,視窗定義中必須有一個 ORDER BY
子句。)
在 RANGE
模式下,這些選項要求 ORDER BY
子句指定正好一個列。offset
指定該列在當前行中的值與幀的前導或後續行中的值之間的最大差值。offset
表示式的資料型別取決於排序列的資料型別。對於數值排序列,它通常與排序列具有相同的型別,但對於日期時間排序列,它是一個 interval
。例如,如果排序列是 date
或 timestamp
型別,可以寫成 RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWING
。offset
仍要求非 NULL 且非負,儘管“非負”的含義取決於其資料型別。
在任何情況下,到幀末尾的距離都受限於到分割槽末尾的距離,因此對於分割槽末尾附近的行,幀可能包含比其他地方少的行。
請注意,在 ROWS
和 GROUPS
模式下,0 PRECEDING
和 0 FOLLOWING
都等同於 CURRENT ROW
。這通常在 RANGE
模式下也成立,並且具有相應資料型別的“零”的含義。
frame_exclusion
選項允許從幀中排除當前行周圍的行,即使它們會根據幀開始和幀結束選項被包含在內。EXCLUDE CURRENT ROW
排除當前行。EXCLUDE GROUP
排除當前行及其排序對等行。EXCLUDE TIES
排除當前行的任何對等行,但不包括當前行本身。EXCLUDE NO OTHERS
僅顯式指定不排除當前行或其對等行的預設行為。
預設的幀選項是 RANGE UNBOUNDED PRECEDING
,它等同於 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
。使用 ORDER BY
時,這會將幀設定為從分割槽開始到當前行的最後一個 ORDER BY
對等行。沒有 ORDER BY
時,這意味著幀包含分割槽的全部行,因為所有行都成為當前行的對等行。
限制是 frame_start
不能是 UNBOUNDED FOLLOWING
,frame_end
不能是 UNBOUNDED PRECEDING
,並且 frame_end
的選擇不能出現在上述 frame_start
和 frame_end
選項列表的順序靠前於 frame_start
的選擇——例如,不允許 RANGE BETWEEN CURRENT ROW AND
。但是,例如,允許 offset
PRECEDINGROWS BETWEEN 7 PRECEDING AND 8 PRECEDING
,即使它永遠不會選擇任何行。
如果指定了 FILTER
,則只有 filter_clause
求值為 true 的輸入行才會被送入視窗函式;其他行將被丟棄。只有是聚合的視窗函式才接受 FILTER
子句。
內建視窗函式在表 9.67中描述。使用者可以新增其他視窗函式。此外,任何內建或使用者定義的通用或統計聚合都可以用作視窗函式。(有序集和假設集聚合目前不能用作視窗函式。)
使用 *
的語法用於將無引數的聚合函式作為視窗函式呼叫,例如 count(*) OVER (PARTITION BY x ORDER BY y)
。星號 (*
) 通常不用於特定於視窗的函式。特定於視窗的函式不允許在函式引數列表中使用 DISTINCT
或 ORDER BY
。
視窗函式呼叫僅允許在查詢的 SELECT
列表和 ORDER BY
子句中。
型別轉換指定從一種資料型別到另一種資料型別的轉換。PostgreSQL 接受兩種等效的型別轉換語法:
CAST (expression
AStype
)expression
::type
CAST
語法符合 SQL;帶有 ::
的語法是歷史上的 PostgreSQL 用法。
當型別轉換應用於已知型別的常量表達式時,它表示執行時型別轉換。只有當定義了合適的型別轉換操作時,轉換才會成功。請注意,這與常量上的轉換用法略有不同,如第 4.1.2.7 節所示。應用於純字串文字的轉換表示字面常量值的初始型別分配,因此它將適用於任何型別(前提是字串文字的內容對於資料型別是可接受的輸入語法)。
如果對值表示式必須產生的型別沒有歧義(例如,當它被賦給表列時),則通常可以省略顯式型別轉換;系統在這種情況下會自動應用型別轉換。但是,自動轉換僅用於在系統目錄中標記為“OK to apply implicitly”的轉換。其他轉換必須使用顯式轉換語法呼叫。此限制旨在防止靜默應用意外的轉換。
也可以使用函式式語法指定型別轉換:
typename
(expression
)
但是,這僅適用於名稱也有效作為函式名稱的型別。例如,double precision
不能這樣使用,但等效的 float8
可以。此外,由於語法衝突,名稱 interval
、time
和 timestamp
只能以這種方式使用,如果它們被雙引號括起來。因此,函式式轉換語法的用法會導致不一致,並且可能應避免。
函式式語法實際上只是一個函式呼叫。當使用兩種標準轉換語法之一進行執行時轉換時,它將內部呼叫已註冊的函式來執行轉換。按照慣例,這些轉換函式與它們的輸出型別同名,因此“函式式語法”只不過是對底層轉換函式的直接呼叫。顯然,這不是可移植應用程式應該依賴的內容。有關更多詳細資訊,請參閱CREATE CAST。
COLLATE
子句覆蓋表示式的排序規則。它附加到它所應用的表示式:
expr
COLLATEcollation
其中 collation
是一個可能帶有模式限定的識別符號。 COLLATE
子句比運算子繫結得更緊;必要時可以使用括號。
如果沒有顯式指定排序規則,則資料庫系統要麼從表示式涉及的列中推匯出排序規則,要麼如果表示式不涉及任何列,則預設為資料庫的預設排序規則。
COLLATE
子句的兩個常見用途是覆蓋 ORDER BY
子句中的排序順序,例如:
SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";
以及覆蓋具有區域設定敏感結果的函式或運算子呼叫的排序規則,例如:
SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";
請注意,在後一種情況下,COLLATE
子句附加到我們希望影響的運算子的輸入引數上。 COLLATE
子句附加到哪個運算子或函式呼叫的引數上並不重要,因為運算子或函式應用的排序規則是透過考慮所有引數推匯出來的,並且顯式的 COLLATE
子句將覆蓋所有其他引數的排序規則。(然而,將不匹配的 COLLATE
子句附加到多個引數上是錯誤的。有關更多詳細資訊,請參閱第 23.2 節。)因此,這與前面的示例結果相同:
SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';
但這將是錯誤的:
SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";
因為它試圖將排序規則應用於 >
運算子的結果,該結果是不可排序的資料型別 boolean
。
標量子查詢是括號中的普通 SELECT
查詢,它返回恰好一行,恰好一列。(有關編寫查詢的資訊,請參閱第 7 章。)執行 SELECT
查詢,並使用返回的單個值作為周圍值表示式的一部分。使用返回多行或多列的查詢作為標量子查詢是錯誤的。(但是,如果在特定執行期間,子查詢未返回任何行,則不會出錯;標量結果將視為 NULL。)子查詢可以引用周圍查詢中的變數,這些變數在子查詢的任何一次求值中都將充當常量。另請參閱第 9.24 節,瞭解涉及子查詢的其他表示式。
例如,以下查詢每個州人口最多的城市:
SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name) FROM states;
陣列構造器是一個表示式,它使用其成員元素的值來構建陣列值。簡單的陣列構造器由關鍵字 ARRAY
、左方括號 [
、表示式列表(用逗號分隔)作為陣列元素值,最後是右方括號 ]
組成。例如:
SELECT ARRAY[1,2,3+4]; array --------- {1,2,7} (1 row)
預設情況下,陣列元素型別是成員表示式的公共型別,使用與 UNION
或 CASE
結構相同的規則確定(請參閱第 10.5 節)。您可以透過顯式將陣列構造器轉換為所需型別來覆蓋此設定,例如:
SELECT ARRAY[1,2,22.7]::integer[]; array ---------- {1,2,23} (1 row)
這與單獨將每個表示式轉換為陣列元素型別具有相同效果。有關型別轉換的更多資訊,請參閱第 4.2.9 節。
多維陣列值可以透過巢狀陣列構造器來構建。在內部構造器中,可以省略關鍵字 ARRAY
。例如,以下產生相同的結果:
SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]]; array --------------- {{1,2},{3,4}} (1 row) SELECT ARRAY[[1,2],[3,4]]; array --------------- {{1,2},{3,4}} (1 row)
由於多維陣列必須是矩形的,因此同一級別的內部構造器必須生成相同維度的子陣列。應用於外部 ARRAY
構造器的任何轉換都會自動傳播到所有內部構造器。
多維陣列構造器元素可以是任何產生正確型別的陣列的內容,不僅僅是子 ARRAY
構造器。例如:
CREATE TABLE arr(f1 int[], f2 int[]); INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]); SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr; array ------------------------------------------------ {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}} (1 row)
您可以構建一個空陣列,但由於無法建立沒有型別的陣列,因此必須顯式將空陣列轉換為所需的型別。例如:
SELECT ARRAY[]::integer[]; array ------- {} (1 row)
也可以從子查詢的結果構建陣列。在此形式中,陣列構造器用關鍵字 ARRAY
後跟一個括號括起來的(不是方括號)子查詢編寫。例如:
SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); array ------------------------------------------------------------------ {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412} (1 row) SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS a(i)); array ---------------------------------- {{1,2},{2,4},{3,6},{4,8},{5,10}} (1 row)
子查詢必須返回單個列。如果子查詢的輸出列是非陣列型別,則生成的(一維)陣列將為子查詢結果中的每一行包含一個元素,其元素型別與子查詢的輸出列的型別匹配。如果子查詢的輸出列是陣列型別,則結果將是一個相同維度但高一維的陣列;在這種情況下,所有子查詢行都必須生成相同維度的陣列,否則結果將不是矩形的。
使用 ARRAY
構建的陣列值下標總是從 1 開始。有關陣列的更多資訊,請參閱第 8.15 節。
行構造器是一個表示式,它使用其成員欄位的值來構建行值(也稱為複合值)。行構造器由關鍵字 ROW
、左括號、零個或多個表示式(用逗號分隔)作為行欄位值,最後是右括號組成。例如:
SELECT ROW(1,2.5,'this is a test');
當列表中有一個以上表達式時,關鍵字 ROW
是可選的。
行構造器可以包含 rowvalue
.*
語法,它將被展開為行值元素的列表,就像在 SELECT
列表的頂層使用 .*
語法時一樣(請參閱第 8.16.5 節)。例如,如果表 t
有列 f1
和 f2
,那麼以下內容是相同的:
SELECT ROW(t.*, 42) FROM t; SELECT ROW(t.f1, t.f2, 42) FROM t;
PostgreSQL 8.2 之前,.*
語法不會在行構造器中展開,因此寫入 ROW(t.*, 42)
會建立一個兩欄位行,其第一個欄位是另一個行值。新行為通常更有用。如果您需要舊的巢狀行值行為,請在不帶 .*
的情況下寫入內部行值,例如 ROW(t, 42)
。
預設情況下,由 ROW
表示式建立的值是匿名記錄型別。如有必要,它可以轉換為命名複合型別——錶行型別或使用 CREATE TYPE AS
建立的複合型別。為了避免歧義,可能需要顯式轉換。例如:
CREATE TABLE mytable(f1 int, f2 float, f3 text); CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; -- No cast needed since only one getf1() exists SELECT getf1(ROW(1,2.5,'this is a test')); getf1 ------- 1 (1 row) CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric); CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; -- Now we need a cast to indicate which function to call: SELECT getf1(ROW(1,2.5,'this is a test')); ERROR: function getf1(record) is not unique SELECT getf1(ROW(1,2.5,'this is a test')::mytable); getf1 ------- 1 (1 row) SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype)); getf1 ------- 11 (1 row)
行構造器可用於構建要儲存在複合型別表列中的複合值,或傳遞給接受複合引數的函式。此外,還可以使用標準比較運算子(如第 9.2 節中所述)測試行,以將一行與另一行進行比較(如第 9.25 節中所述),並在子查詢中使用它們(如第 9.24 節中所討論)。
子表示式的求值順序未定義。特別是,運算子或函式的輸入不一定按從左到右或任何其他固定順序求值。
此外,如果表示式的結果可以透過僅求值其中的一部分來確定,則其他子表示式可能根本不被求值。例如,如果有人寫:
SELECT true OR somefunc();
那麼 somefunc()
(可能)根本不會被呼叫。如果有人寫:
SELECT somefunc() OR true;
請注意,這與某些程式語言中的布林運算子的從左到右“短路”不同。
因此,將具有副作用的函式用作複雜表示式的一部分是不明智的。WHERE
和 HAVING
子句中的副作用或求值順序尤其危險,因為這些子句在制定執行計劃時會被大量重新處理。這些子句中的布林表示式(AND
/OR
/NOT
組合)可以以布林代數定律允許的任何方式重新組織。
當強制求值順序至關重要時,可以使用 CASE
構造(請參閱第 9.18 節)。例如,在 WHERE
子句中嘗試避免除以零的方法是不可信的:
SELECT ... WHERE x > 0 AND y/x > 1.5;
但這很安全:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
以這種方式使用的 CASE
構造將挫敗最佳化嘗試,因此只有在必要時才應執行。(在此特定示例中,最好透過寫入 y > 1.5*x
來規避該問題。)
CASE
並不是解決此類問題的萬能藥。上述技術的一個限制是它不能防止常量子表示式的早期求值。如第 36.7 節所述,標記為 IMMUTABLE
的函式和運算子可以在查詢規劃時而不是執行時進行求值。因此,例如:
SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;
可能會導致除零錯誤,因為規劃器會嘗試簡化常量子表示式,即使表中的每一行都有 x > 0
,使得 ELSE
分支在執行時永遠不會被進入。
雖然這個特定的例子可能看起來很愚蠢,但涉及常量的相關情況可能發生在函式內執行的查詢中,因為函式引數和區域性變數的值可以作為常量插入到查詢中進行規劃。例如,在 PL/pgSQL 函式中,使用 IF
-THEN
-ELSE
語句來保護有風險的計算比將其巢狀在 CASE
表示式中安全得多。
同樣型別的另一個限制是 CASE
不能阻止其中包含的聚合表示式的求值,因為聚合表示式在考慮 SELECT
列表或 HAVING
子句中的其他表示式之前進行計算。例如,以下查詢可能會導致除零錯誤,儘管看似已受到保護:
SELECT CASE WHEN min(employees) > 0 THEN avg(expenses / employees) END FROM departments;
min()
和 avg()
聚合函式在所有輸入行上同時計算,因此如果任何行的 employees
為零,則除零錯誤將在 min()
結果有機會被測試之前發生。相反,使用 WHERE
或 FILTER
子句可以防止有問題的輸入行到達聚合函式。
如果您在文件中看到任何不正確、與您對特定功能的使用經驗不符或需要進一步澄清的內容,請使用此表單報告文件問題。