數值運算式用於多種情境,例如 SELECT
命令的目標清單中,作為 INSERT
或 UPDATE
中的新欄位值,或在多個命令的搜尋條件中。數值運算式的結果有時稱為純量,以區分它與表格運算式(也就是表格)的結果。因此,數值運算式也稱為純量運算式(或簡稱為運算式)。運算式語法允許使用算術、邏輯、集合和其他運算,從基本部分計算數值。
數值運算式為以下其中之一
除了此清單之外,還有許多可以分類為運算式的建構,但不遵循任何通用語法規則。這些通常具有函數或運算子的語意,並在第 9 章的適當位置進行說明。一個例子是 IS NULL
子句。
我們已在第 4.1.2 節中討論了常數。以下各節討論其餘選項。
可以使用以下形式參照欄位
correlation
.columnname
correlation
是表格的名稱(可能以結構描述名稱限定),或以 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
]
(此處,方括號 [ ]
表示按字面顯示。)每個subscript
本身都是一個運算式,將四捨五入到最接近的整數值。
一般來說,陣列expression
必須加上括號,但當要加下標的運算式只是欄位參照或位置參數時,可以省略括號。此外,當原始陣列是多維時,可以串連多個下標。例如
mytable.arraycolumn[4] mytable.two_d_column[17][34] $1[10:42] (arrayfunction(a,b))[42]
最後一個範例中需要括號。有關陣列的更多資訊,請參閱第 8.15 節。
如果運算式產生複合型別(列型別)的數值,則可以透過撰寫以下內容來擷取列的特定欄位
expression
.fieldname
一般來說,列expression
必須加上括號,但當要從中選取的運算式只是表格參照或位置參數時,可以省略括號。例如
mytable.mycolumn $1.somecolumn (rowfunction(a,b)).col3
(因此,限定欄位參照實際上只是欄位選擇語法的特例。)一個重要的特例是從複合型別的表格欄位中擷取欄位
(compositecol).somefield (mytable.compositecol).somefield
此處需要括號,以表明 compositecol
是一個欄位名稱,而不是表格名稱;或者在第二種情況下,表明 mytable
是一個表格名稱,而不是結構描述名稱。
您可以寫入 .*
來要求複合值的所有欄位。
(compositecol).*
此表示法根據上下文而有不同的行為;詳情請參閱第 8.16.5 節。
運算子調用有兩種可能的語法
expression operator expression (二元中綴運算子) |
operator expression (一元前綴運算子) |
其中 operator
符號遵循第 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 的列。對於所有內建彙總,除非另有說明,否則可以假設為 true。
例如,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
金鑰的單一引數彙總函式的呼叫(第二個金鑰由於是常數而相當無用)。
如果在 order_by_clause
中指定了 DISTINCT
,則 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
必須產生一個非空、非負的整數,並且該選項表示框架從目前列之前或之後的指定行數開始或結束。
在 GROUPS
模式下,offset
同樣必須產生一個非空、非負的整數,並且該選項表示框架從目前列的對等群組之前或之後的指定數量的對等群組開始或結束,其中對等群組是在 ORDER BY
排序中等效的一組列。(必須在視窗定義中使用 ORDER BY
子句才能使用 GROUPS
模式。)
在 RANGE
模式下,這些選項要求 ORDER BY
子句僅指定一個欄位。offset
指定目前列中該欄位的值與框架中前導或後續列中的值之間的差值上限。offset
表達式的資料類型取決於排序欄位的資料類型。對於數值排序欄位,它通常與排序欄位的類型相同,但對於日期時間排序欄位,它是一個 interval
。例如,如果排序欄位的類型為 date
或 timestamp
,則可以寫成 RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWING
。offset
仍然需要是非空且非負數,儘管 “非負數” 的含義取決於其資料類型。
無論如何,到框架末尾的距離都受到到分割區末尾的距離限制,因此對於靠近分割區末尾的列,框架可能包含比其他地方更少的列。
請注意,在 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.65中描述。使用者可以新增其他的視窗函數。此外,任何內建或使用者定義的通用或統計彙總函數都可以用作視窗函數。(目前無法將已排序的集合和假設集合彙總用作視窗函數。)
使用 *
的語法用於將無參數彙總函數作為視窗函數呼叫,例如 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 節所示。套用到未修飾的字串文字的轉換表示將類型初始分配給文字常數值,因此對於任何類型都將成功(如果字串文字的內容是資料類型可接受的輸入語法)。
如果數值表達式必須產生的類型沒有歧義(例如,當將其分配給表欄位時),通常可以省略顯式的類型轉換;在這種情況下,系統會自動套用類型轉換。但是,僅對在系統目錄中標記為 “可以隱式套用” 的轉換執行自動轉換。其他轉換必須使用顯式轉換語法來呼叫。此限制旨在防止靜默套用令人驚訝的轉換。
也可以使用類似函數的語法來指定類型轉換
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
子句來防止有問題的輸入列首先到達彙總函式。
如果您在文件中發現任何不正確、與特定功能的體驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。