PostgreSQL 值表達(dá)式

2021-08-25 18:15 更新
4.2.1. 列引用
4.2.2. 位置參數(shù)
4.2.3. 下標(biāo)
4.2.4. 域選擇
4.2.5. 操作符調(diào)用
4.2.6. 函數(shù)調(diào)用
4.2.7. 聚集表達(dá)式
4.2.8. 窗口函數(shù)調(diào)用
4.2.9. 類(lèi)型轉(zhuǎn)換
4.2.10. 排序規(guī)則表達(dá)式
4.2.11. 標(biāo)量子查詢(xún)
4.2.12. 數(shù)組構(gòu)造器
4.2.13. 行構(gòu)造器
4.2.14. 表達(dá)式計(jì)算規(guī)則

值表達(dá)式被用于各種各樣的環(huán)境中,例如在SELECT命令的目標(biāo)列表中、作為INSERTUPDATE中的新列值或者若干命令中的搜索條件。為了區(qū)別于一個(gè)表表達(dá)式(是一個(gè)表)的結(jié)果,一個(gè)值表達(dá)式的結(jié)果有時(shí)候被稱(chēng)為一個(gè)標(biāo)量。值表達(dá)式因此也被稱(chēng)為標(biāo)量表達(dá)式(或者甚至簡(jiǎn)稱(chēng)為 表達(dá)式)。表達(dá)式語(yǔ)法允許使用算數(shù)、邏輯、集合和其他操作從原始部分計(jì)算值。

一個(gè)值表達(dá)式是下列之一:

  • 一個(gè)常量或文字值

  • 一個(gè)列引用

  • 在一個(gè)函數(shù)定義體或預(yù)備語(yǔ)句中的一個(gè)位置參數(shù)引用

  • 一個(gè)下標(biāo)表達(dá)式

  • 一個(gè)域選擇表達(dá)式

  • 一個(gè)操作符調(diào)用

  • 一個(gè)函數(shù)調(diào)用

  • 一個(gè)聚集表達(dá)式

  • 一個(gè)窗口函數(shù)調(diào)用

  • 一個(gè)類(lèi)型轉(zhuǎn)換

  • 一個(gè)排序規(guī)則表達(dá)式

  • 一個(gè)標(biāo)量子查詢(xún)

  • 一個(gè)數(shù)組構(gòu)造器

  • 一個(gè)行構(gòu)造器

  • 另一個(gè)在圓括號(hào)(用來(lái)分組子表達(dá)式以及重載優(yōu)先級(jí) )中的值表達(dá)式

在這個(gè)列表之外,還有一些結(jié)構(gòu)可以被分類(lèi)為一個(gè)表達(dá)式,但是它們不遵循任何一般語(yǔ)法規(guī)則。這些通常具有一個(gè)函數(shù)或操作符的語(yǔ)義并且在第 9 章中的合適位置解釋。一個(gè)例子是IS NULL子句。

我們已經(jīng)在第 4.1.2 節(jié)中討論過(guò)常量。下面的小節(jié)會(huì)討論剩下的選項(xiàng)。

4.2.1. 列引用

一個(gè)列可以以下面的形式被引用:

correlation.columnname

correlation是一個(gè)表(有可能以一個(gè)模式名限定)的名字,或者是在FROM子句中為一個(gè)表定義的別名。如果列名在當(dāng)前索引所使用的表中都是唯一的,關(guān)聯(lián)名稱(chēng)和分隔用的句點(diǎn)可以被忽略(另見(jiàn)第 7 章)。

4.2.2. 位置參數(shù)

一個(gè)位置參數(shù)引用被用來(lái)指示一個(gè)由 SQL 語(yǔ)句外部提供的值。參數(shù)被用于 SQL 函數(shù)定義和預(yù)備查詢(xún)中。某些客戶(hù)端庫(kù)還支持獨(dú)立于 SQL 命令字符串來(lái)指定數(shù)據(jù)值,在這種情況中參數(shù)被用來(lái)引用那些線(xiàn)外數(shù)據(jù)值。一個(gè)參數(shù)引用的形式是:

$number

例如,考慮一個(gè)函數(shù)dept的定義:

CREATE FUNCTION dept(text) RETURNS dept
    AS $$ SELECT * FROM dept WHERE name = $1 $$
    LANGUAGE SQL;

這里$1引用函數(shù)被調(diào)用時(shí)第一個(gè)函數(shù)參數(shù)的值。

4.2.3. 下標(biāo)

如果一個(gè)表達(dá)式得到了一個(gè)數(shù)組類(lèi)型的值,那么可以抽取出該數(shù)組值的一個(gè)特定元素:

expression[subscript]

或者抽取出多個(gè)相鄰元素(一個(gè)數(shù)組切片):

expression[lower_subscript:upper_subscript]

(這里,方括號(hào)[ ]表示其字面意思)。每一個(gè)下標(biāo)自身是一個(gè)表達(dá)式,它將四舍五入到最接近的整數(shù)值。

通常,數(shù)組表達(dá)式必須被加上括號(hào),但是當(dāng)要被加下標(biāo)的表達(dá)式只是一個(gè)列引用或位置參數(shù)時(shí),括號(hào)可以被忽略。還有,當(dāng)原始數(shù)組是多維時(shí),多個(gè)下標(biāo)可以被連接起來(lái)。例如:

mytable.arraycolumn[4]
mytable.two_d_column[17][34]
$1[10:42]
(arrayfunction(a,b))[42]

最后一個(gè)例子中的圓括號(hào)是必需的。詳見(jiàn)第 8.15 節(jié)。

4.2.4. 域選擇

如果一個(gè)表達(dá)式得到一個(gè)組合類(lèi)型(行類(lèi)型)的值,那么可以抽取該行的指定域

expression.fieldname

通常行表達(dá)式必須被加上括號(hào),但是當(dāng)該表達(dá)式是僅從一個(gè)表引用或位置參數(shù)選擇時(shí),圓括號(hào)可以被忽略。例如:

mytable.mycolumn
$1.somecolumn
(rowfunction(a,b)).col3

(因此,一個(gè)被限定的列引用實(shí)際上只是域選擇語(yǔ)法的一種特例)。一種重要的特例是從一個(gè)組合類(lèi)型的表列中抽取一個(gè)域:

(compositecol).somefield
(mytable.compositecol).somefield

這里需要圓括號(hào)來(lái)顯示compositecol是一個(gè)列名而不是一個(gè)表名,在第二種情況中則是顯示mytable是一個(gè)表名而不是一個(gè)模式名。

你可以通過(guò)書(shū)寫(xiě).*來(lái)請(qǐng)求一個(gè)組合值的所有域:

(compositecol).*

這種記法的行為根據(jù)上下文會(huì)有不同,詳見(jiàn)第 8.16.5 節(jié)。

4.2.5. 操作符調(diào)用

對(duì)于一次操作符調(diào)用,有三種可能的語(yǔ)法:

expression operator expression(二元中綴操作符)
operator expression(一元前綴操作符)
expression operator(一元后綴操作符)

其中operator記號(hào)遵循第 4.1.3 節(jié)的語(yǔ)法規(guī)則,或者是關(guān)鍵詞ANDORNOT之一,或者是一個(gè)如下形式的受限定操作符名:

OPERATOR(schema.operatorname)

哪個(gè)特定操作符存在以及它們是一元的還是二元的取決于由系統(tǒng)或用戶(hù)定義的那些操作符。第 9 章描述了內(nèi)建操作符。

4.2.6. 函數(shù)調(diào)用

一個(gè)函數(shù)調(diào)用的語(yǔ)法是一個(gè)函數(shù)的名稱(chēng)(可能受限于一個(gè)模式名)后面跟上封閉于圓括號(hào)中的參數(shù)列表:

function_name ([expression [, expression ... ]] )

例如,下面會(huì)計(jì)算 2 的平方根:

sqrt(2)

當(dāng)在一個(gè)某些用戶(hù)不信任其他用戶(hù)的數(shù)據(jù)庫(kù)中發(fā)出查詢(xún)時(shí),在編寫(xiě)函數(shù)調(diào)用時(shí)應(yīng)遵守第 10.3 節(jié)中的安全防范措施。

內(nèi)建函數(shù)的列表在第 9 章中。其他函數(shù)可以由用戶(hù)增加。

參數(shù)可以有選擇地被附加名稱(chēng)。詳見(jiàn)第 4.3 節(jié)

注意

一個(gè)采用單一組合類(lèi)型參數(shù)的函數(shù)可以被有選擇地稱(chēng)為域選擇語(yǔ)法,并且反過(guò)來(lái)域選擇可以被寫(xiě)成函數(shù)的風(fēng)格。也就是說(shuō),記號(hào)col(table)table.col是可以互換的。這種行為是非 SQL 標(biāo)準(zhǔn)的但是在PostgreSQL中被提供,因?yàn)樗试S函數(shù)的使用來(lái)模擬計(jì)算域。詳見(jiàn) 第 8.16.5 節(jié)。

4.2.7. 聚集表達(dá)式

一個(gè)聚集表達(dá)式表示在由一個(gè)查詢(xún)選擇的行上應(yīng)用一個(gè)聚集函數(shù)。一個(gè)聚集函數(shù)將多個(gè)輸入減少到一個(gè)單一輸出值,例如對(duì)輸入的求和或平均。一個(gè)聚集表達(dá)式的語(yǔ)法是下列之一:

aggregate_name (expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name (ALL expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name ( [ expression [ , ... ] ] ) WITHIN GROUP ( order_by_clause ) [ FILTER ( WHERE filter_clause ) ]

這里aggregate_name是一個(gè)之前定義的聚集(可能帶有一個(gè)模式名限定),并且expression是任意自身不包含聚集表達(dá)式的值表達(dá)式或一個(gè)窗口函數(shù)調(diào)用??蛇x的order_by_clausefilter_clause描述如下。

第一種形式的聚集表達(dá)式為每一個(gè)輸入行調(diào)用一次聚集。第二種形式和第一種相同,因?yàn)?code class="literal">ALL是默認(rèn)選項(xiàng)。第三種形式為輸入行中表達(dá)式的每一個(gè)可區(qū)分值(或者對(duì)于多個(gè)表達(dá)式是值的可區(qū)分集合)調(diào)用一次聚集。第四種形式為每一個(gè)輸入行調(diào)用一次聚集,因?yàn)闆](méi)有特定的輸入值被指定,它通常只對(duì)于count(*)聚集函數(shù)有用。最后一種形式被用于有序集聚集函數(shù),其描述如下。

大部分聚集函數(shù)忽略空輸入,這樣其中一個(gè)或多個(gè)表達(dá)式得到空值的行將被丟棄。除非另有說(shuō)明,對(duì)于所有內(nèi)建聚集都是這樣。

例如,count(*)得到輸入行的總數(shù)。count(f1)得到輸入行中f1為非空的數(shù)量,因?yàn)?code class="function">count忽略空值。而count(distinct f1)得到f1的非空可區(qū)分值的數(shù)量。

一般地,交給聚集函數(shù)的輸入行是未排序的。在很多情況中這沒(méi)有關(guān)系,例如不管接收到什么樣的輸入,min總是產(chǎn)生相同的結(jié)果。但是,某些聚集函數(shù)(例如array_aggstring_agg)依據(jù)輸入行的排序產(chǎn)生結(jié)果。當(dāng)使用這類(lèi)聚集時(shí),可選的order_by_clause可以被用來(lái)指定想要的順序。 order_by_clause 與查詢(xún)級(jí)別的ORDER BY子句(如第 7.5 節(jié)所述)具有相同的語(yǔ)法,除非它的表達(dá)式總是僅有表達(dá)式并且不能是輸出列名稱(chēng)或編號(hào)。例如:

SELECT array_agg(a ORDER BY b DESC) FROM table;

在處理多參數(shù)聚集函數(shù)時(shí),注意ORDER BY出現(xiàn)在所有聚集參數(shù)之后。例如,要這樣寫(xiě):

SELECT string_agg(a, ',' ORDER BY a) FROM table;

而不能這樣寫(xiě):

SELECT string_agg(a ORDER BY a, ',') FROM table;  -- 不正確

后者在語(yǔ)法上是合法的,但是它表示用兩個(gè)ORDER BY鍵來(lái)調(diào)用一個(gè)單一參數(shù)聚集函數(shù)(第二個(gè)是無(wú)用的,因?yàn)樗且粋€(gè)常量)。

如果在order_by_clause之外指定了DISTINCT,那么所有的ORDER BY表達(dá)式必須匹配聚集的常規(guī)參數(shù)。也就是說(shuō),你不能在DISTINCT列表沒(méi)有包括的表達(dá)式上排序。

注意

在一個(gè)聚集函數(shù)中指定DISTINCT以及ORDER BY的能力是一種PostgreSQL擴(kuò)展。

按照到目前為止的描述,如果一般目的和統(tǒng)計(jì)性聚集中 排序是可選的,在要為它排序輸入行時(shí)可以在該聚集的常規(guī)參數(shù) 列表中放置ORDER BY。有一個(gè)聚集函數(shù)的子集叫 做有序集聚集,它要求一個(gè) order_by_clause,通常是因?yàn)? 該聚集的計(jì)算只對(duì)其輸入行的特定順序有意義。有序集聚集的典 型例子包括排名和百分位計(jì)算。按照上文的最后一種語(yǔ)法,對(duì)于 一個(gè)有序集聚集, order_by_clause被寫(xiě)在 WITHIN GROUP (...)中。 order_by_clause中的表達(dá)式 會(huì)像普通聚集參數(shù)一樣對(duì)每一個(gè)輸入行計(jì)算一次,按照每個(gè) order_by_clause 的要求排序并 且交給該聚集函數(shù)作為輸入?yún)?shù)(這和非 WITHIN GROUP order_by_clause的情況不同,在其中表達(dá) 式的結(jié)果不會(huì)被作為聚集函數(shù)的參數(shù))。如果有在 WITHIN GROUP之前的參數(shù)表達(dá)式,會(huì)把它們稱(chēng) 為直接參數(shù)以便與列在 order_by_clause 中的 聚集參數(shù)相區(qū)分。與普通聚集參數(shù)不同,針對(duì) 每次聚集調(diào)用只會(huì)計(jì)算一次直接參數(shù),而不是為每一個(gè)輸入行 計(jì)算一次。這意味著只有那些變量被GROUP BY 分組時(shí),它們才能包含這些變量。這個(gè)限制同樣適用于根本不在 一個(gè)聚集表達(dá)式內(nèi)部的直接參數(shù)。直接參數(shù)通常被用于百分?jǐn)?shù) 之類(lèi)的東西,它們只有作為每次聚集計(jì)算用一次的單一值才有意 義。直接參數(shù)列表可以為空,在這種情況下,寫(xiě)成() 而不是 (*)(實(shí)際上 PostgreSQL接受兩種拼寫(xiě),但是只有第一 種符合 SQL 標(biāo)準(zhǔn))。

有序集聚集的調(diào)用例子:

SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households;
 percentile_cont
-----------------
           50489

這會(huì)從表householdsincome列得到第 50 個(gè)百分位或者中位的值。 這里0.5是一個(gè)直接參數(shù),對(duì)于百分位部分是一個(gè) 在不同行之間變化的值的情況它沒(méi)有意義。

如果指定了FILTER,那么只有對(duì)filter_clause計(jì)算為真的輸入行會(huì)被交給該聚集函數(shù),其他行會(huì)被丟棄。例如:

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)

預(yù)定義的聚集函數(shù)在第 9.21 節(jié)中描述。其他聚集函數(shù)可以由用戶(hù)增加。

一個(gè)聚集表達(dá)式只能出現(xiàn)在SELECT命令的結(jié)果列表或是HAVING子句中。在其他子句(如WHERE)中禁止使用它,因?yàn)槟切┳泳涞挠?jì)算在邏輯上是在聚集的結(jié)果被形成之前。

當(dāng)一個(gè)聚集表達(dá)式出現(xiàn)在一個(gè)子查詢(xún)中(見(jiàn)第 4.2.11 節(jié)第 9.23 節(jié)),聚集通常在該子查詢(xún)的行上被計(jì)算。但是如果該聚集的參數(shù)(以及 filter_clause ,如果有)只包含外層變量則會(huì)產(chǎn)生一個(gè)異常:該聚集則屬于最近的那個(gè)外層,并且會(huì)在那個(gè)查詢(xún)的行上被計(jì)算。該聚集表達(dá)式從整體上則是對(duì)其所出現(xiàn)于的子查詢(xún)的一種外層引用,并且在那個(gè)子查詢(xún)的任意一次計(jì)算中都作為一個(gè)常量。只出現(xiàn)在結(jié)果列表或HAVING子句的限制適用于該聚集所屬的查詢(xún)層次。

4.2.8. 窗口函數(shù)調(diào)用

一個(gè)窗口函數(shù)調(diào)用表示在一個(gè)查詢(xún)選擇的行的某個(gè)部分上應(yīng)用一個(gè)聚集類(lèi)的函數(shù)。和非窗口聚集函數(shù)調(diào)用不同,這不會(huì)被約束為將被選擇的行分組為一個(gè)單一的輸出行 — 在查詢(xún)輸出中每一個(gè)行仍保持獨(dú)立。不過(guò),窗口函數(shù)能夠根據(jù)窗口函數(shù)調(diào)用的分組聲明(PARTITION BY列表)訪(fǎng)問(wèn)屬于當(dāng)前行所在分組中的所有行。一個(gè)窗口函數(shù)調(diào)用的語(yǔ)法是下列之一:

function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )

其中window_definition的語(yǔ)法是

[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]

可選的frame_clause是下列之一

{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]

其中frame_startframe_end可以是下面形式中的一種

UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING

frame_exclusion可以是下列之一

EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

這里,expression表示任何自身不含有窗口函數(shù)調(diào)用的值表達(dá)式。

window_name是對(duì)定義在查詢(xún)的WINDOW子句中的一個(gè)命名窗口聲明的引用。還可以使用在WINDOW子句中定義命名窗口的相同語(yǔ)法在圓括號(hào)內(nèi)給定一個(gè)完整的window_definition,詳見(jiàn)SELECT參考頁(yè)。值得指出的是,OVER wname并不嚴(yán)格地等價(jià)于OVER (wname ...),后者表示復(fù)制并修改窗口定義,并且在被引用窗口聲明包括一個(gè)幀子句時(shí)會(huì)被拒絕。

PARTITION BY選項(xiàng)將查詢(xún)的行分組成為分區(qū),窗口函數(shù)會(huì)獨(dú)立地處理它們。PARTITION BY工作起來(lái)類(lèi)似于一個(gè)查詢(xún)級(jí)別的GROUP BY子句,不過(guò)它的表達(dá)式總是只是表達(dá)式并且不能是輸出列的名稱(chēng)或編號(hào)。如果沒(méi)有PARTITION BY,該查詢(xún)產(chǎn)生的所有行被當(dāng)作一個(gè)單一分區(qū)來(lái)處理。 ORDER BY選項(xiàng)決定被窗口函數(shù)處理的一個(gè)分區(qū)中的行的順序。它工作起來(lái)類(lèi)似于一個(gè)查詢(xún)級(jí)別的ORDER BY子句,但是同樣不能使用輸出列的名稱(chēng)或編號(hào)。如果沒(méi)有ORDER BY,行將被以未指定的順序被處理。

frame_clause指定構(gòu)成窗口幀的行集合,它是當(dāng)前分區(qū)的一個(gè)子集,窗口函數(shù)將作用在該幀而不是整個(gè)分區(qū)。幀中的行集合會(huì)隨著哪一行是當(dāng)前行而變化。在RANGE、ROWS或者GROUPS模式中可以指定幀,在每一種情況下,幀的范圍都是從 frame_start frame_end。如果frame_end被省略,則末尾默認(rèn)為CURRENT ROW。

UNBOUNDED PRECEDING的一個(gè)frame_start表示該幀開(kāi)始于分區(qū)的第一行,類(lèi)似地UNBOUNDED FOLLOWING的一個(gè)frame_end表示該幀結(jié)束于分區(qū)的最后一行。

RANGEGROUPS模式中,CURRENT ROW的一個(gè)frame_start表示幀開(kāi)始于當(dāng)前行的第一個(gè)平級(jí)行(被窗口的ORDER BY子句排序?yàn)榕c當(dāng)前行等效的行),而 CURRENT ROW的一個(gè)frame_end表示幀結(jié)束于當(dāng)前行的最后一個(gè)平級(jí)行。在ROWS模式中,CURRENT ROW就表示當(dāng)前行。

offset PRECEDING以及offset FOLLOWING幀選項(xiàng)中,offset必須是一個(gè)不包含任何變量、聚集函數(shù)或者窗口函數(shù)的表達(dá)式。 offset 的含義取決于幀模式:

  • ROWS模式中,offset必須得到一個(gè)非空、非負(fù)的整數(shù),并且該選項(xiàng)表示幀開(kāi)始于當(dāng)前行之前或者之后指定數(shù)量的行。

  • GROUPS模式中,offset也必須得到一個(gè)非空、非負(fù)的整數(shù),并且該選項(xiàng)表示幀開(kāi)始于當(dāng)前行的平級(jí)組之前或者之后指定數(shù)量的平級(jí)組,這里平級(jí)組是在ORDER BY順序中等效的行集合(要使用GROUPS模式,在窗口定義中就必須有一個(gè) ORDER BY子句)。

  • RANGE模式中,這些選項(xiàng)要求ORDER BY子句正好指定一列。offset指定當(dāng)前行中那一列的值與它在該幀中前面或后面的行中的列值的最大差值。offset表達(dá)式的數(shù)據(jù)類(lèi)型會(huì)隨著排序列的數(shù)據(jù)類(lèi)型而變化。對(duì)于數(shù)字的排序列,它通常是與排序列相同的類(lèi)型,但對(duì)于日期時(shí)間排序列它是一個(gè) interval。例如,如果排序列是類(lèi)型date或者timestamp,我們可以寫(xiě)RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWINGoffset仍然要求是非空且非負(fù),不過(guò) 非負(fù)的含義取決于它的數(shù)據(jù)類(lèi)型。

在任何一種情況下,到幀末尾的距離都受限于到分區(qū)末尾的距離,因此對(duì)于離分區(qū)末尾比較近的行來(lái)說(shuō),幀可能會(huì)包含比較少的行。

注意在ROWS以及GROUPS模式中,0 PRECEDING0 FOLLOWINGCURRENT ROW等效。通常在RANGE模式中,這個(gè)結(jié)論也成立(只要有一種合適的、與數(shù)據(jù)類(lèi)型相關(guān)的 的含義)。

frame_exclusion選項(xiàng)允許當(dāng)前行周?chē)男斜慌懦趲?,即便根?jù)幀的開(kāi)始和結(jié)束選項(xiàng)應(yīng)該把它們包括在幀中。EXCLUDE CURRENT ROW會(huì)把當(dāng)前行排除在幀之外。EXCLUDE GROUP會(huì)把當(dāng)前行以及它在順序上的平級(jí)行都排除在幀之外。EXCLUDE TIES把當(dāng)前行的任何平級(jí)行都從幀中排除,但不排除當(dāng)前行本身。 EXCLUDE NO OTHERS只是明確地指定不排除當(dāng)前行或其平級(jí)行的這種默認(rèn)行為。

默認(rèn)的幀選項(xiàng)是RANGE UNBOUNDED PRECEDING,它和RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。如果使用ORDER BY,這會(huì)把該幀設(shè)置為從分區(qū)開(kāi)始一直到當(dāng)前行的最后一個(gè)ORDER BY平級(jí)行的所有行。如果不使用 ORDER BY,就意味著分區(qū)中所有的行都被包括在窗口幀中,因?yàn)樗行卸汲蔀榱水?dāng)前行的平級(jí)行。

限制是frame_start不能是UNBOUNDED FOLLOWING、frame_end不能是UNBOUNDED PRECEDING,并且在上述frame_startframe_end 選項(xiàng)的列表中frame_end選擇不能早于frame_start選擇出現(xiàn) — 例如不允許RANGE BETWEEN CURRENT ROW AND offset PRECEDING,但允許ROWS BETWEEN 7 PRECEDING AND 8 PRECEDING,雖然它不會(huì)選擇任何行。

如果指定了FILTER,那么只有對(duì)filter_clause計(jì)算為真的輸入行會(huì)被交給該窗口函數(shù),其他行會(huì)被丟棄。只有是聚集的窗口函數(shù)才接受FILTER 。

內(nèi)建的窗口函數(shù)在表 9.60中介紹。用戶(hù)可以加入其他窗口函數(shù)。此外,任何內(nèi)建的或者用戶(hù)定義的通用聚集或者統(tǒng)計(jì)性聚集都可以被用作窗口函數(shù)(有序集和假想集聚集當(dāng)前不能被用作窗口函數(shù))。

使用*的語(yǔ)法被用來(lái)把參數(shù)較少的聚集函數(shù)當(dāng)作窗口函數(shù)調(diào)用,例如count(*) OVER (PARTITION BY x ORDER BY y)。星號(hào)(*)通常不被用于窗口相關(guān)的函數(shù)。窗口相關(guān)的函數(shù)不允許在函數(shù)參數(shù)列表中使用DISTINCTORDER BY。

只有在SELECT列表和查詢(xún)的ORDER BY子句中才允許窗口函數(shù)調(diào)用。

更多關(guān)于窗口函數(shù)的信息可以在第 3.5 節(jié)、第 9.22 節(jié)以及第 7.2.5 節(jié)中找到。

4.2.9. 類(lèi)型轉(zhuǎn)換

一個(gè)類(lèi)型造型指定從一種數(shù)據(jù)類(lèi)型到另一種數(shù)據(jù)類(lèi)型的轉(zhuǎn)換。PostgreSQL接受兩種等價(jià)的類(lèi)型造型語(yǔ)法:

CAST ( expression AS type )
expression::type

CAST語(yǔ)法遵從 SQL,而用::的語(yǔ)法是PostgreSQL的歷史用法。

當(dāng)一個(gè)造型被應(yīng)用到一種未知類(lèi)型的值表達(dá)式上時(shí),它表示一種運(yùn)行時(shí)類(lèi)型轉(zhuǎn)換。只有已經(jīng)定義了一種合適的類(lèi)型轉(zhuǎn)換操作時(shí),該造型才會(huì)成功。注意這和常量的造型(如第 4.1.2.7 節(jié)中所示)使用不同。應(yīng)用于一個(gè)未修飾串文字的造型表示一種類(lèi)型到一個(gè)文字常量值的初始賦值,并且因此它將對(duì)任意類(lèi)型都成功(如果該串文字的內(nèi)容對(duì)于該數(shù)據(jù)類(lèi)型的輸入語(yǔ)法是可接受的)。

如果一個(gè)值表達(dá)式必須產(chǎn)生的類(lèi)型沒(méi)有歧義(例如當(dāng)它被指派給一個(gè)表列),通??梢允÷燥@式類(lèi)型造型,在這種情況下系統(tǒng)會(huì)自動(dòng)應(yīng)用一個(gè)類(lèi)型造型。但是,只有對(duì)在系統(tǒng)目錄中被標(biāo)記為OK to apply implicitly的造型才會(huì)執(zhí)行自動(dòng)造型。其他造型必須使用顯式造型語(yǔ)法調(diào)用。這種限制是為了防止出人意料的轉(zhuǎn)換被無(wú)聲無(wú)息地應(yīng)用。

還可以用像函數(shù)的語(yǔ)法來(lái)指定一次類(lèi)型造型:

typename ( expression )

不過(guò),這只對(duì)那些名字也作為函數(shù)名可用的類(lèi)型有效。例如,double precision不能以這種方式使用,但是等效的float8可以。還有,如果名稱(chēng)interval、timetimestamp被用雙引號(hào)引用,那么由于語(yǔ)法沖突的原因,它們只能以這種風(fēng)格使用。因此,函數(shù)風(fēng)格的造型語(yǔ)法的使用會(huì)導(dǎo)致不一致性并且應(yīng)該盡可能被避免。

注意

函數(shù)風(fēng)格的語(yǔ)法事實(shí)上只是一次函數(shù)調(diào)用。當(dāng)兩種標(biāo)準(zhǔn)造型語(yǔ)法之一被用來(lái)做一次運(yùn)行時(shí)轉(zhuǎn)換時(shí),它將在內(nèi)部調(diào)用一個(gè)已注冊(cè)的函數(shù)來(lái)執(zhí)行該轉(zhuǎn)換。簡(jiǎn)而言之,這些轉(zhuǎn)換函數(shù)具有和它們的輸出類(lèi)型相同的名字,并且因此函數(shù)風(fēng)格的語(yǔ)法無(wú)非是對(duì)底層轉(zhuǎn)換函數(shù)的一次直接調(diào)用。顯然,一個(gè)可移植的應(yīng)用不應(yīng)當(dāng)依賴(lài)于它。詳見(jiàn) CREATE CAST 。

4.2.10. 排序規(guī)則表達(dá)式

COLLATE子句會(huì)重載一個(gè)表達(dá)式的排序規(guī)則。它被追加到它適用的表達(dá)式:

expr COLLATE collation

這里collation可能是一個(gè)受模式限定的標(biāo)識(shí)符。COLLATE子句比操作符綁得更緊,需要時(shí)可以使用圓括號(hào)。

如果沒(méi)有顯式指定排序規(guī)則,數(shù)據(jù)庫(kù)系統(tǒng)會(huì)從表達(dá)式所涉及的列中得到一個(gè)排序規(guī)則,如果該表達(dá)式?jīng)]有涉及列,則會(huì)默認(rèn)采用數(shù)據(jù)庫(kù)的默認(rèn)排序規(guī)則。

COLLATE子句的兩種常見(jiàn)使用是重載ORDER BY子句中的排序順序,例如:

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

以及重載具有區(qū)域敏感結(jié)果的函數(shù)或操作符調(diào)用的排序規(guī)則,例如:

SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";

注意在后一種情況中,COLLATE子句被附加到我們希望影響的操作符的一個(gè)輸入?yún)?shù)上。COLLATE子句被附加到該操作符或函數(shù)調(diào)用的哪個(gè)參數(shù)上無(wú)關(guān)緊要,因?yàn)楸徊僮鞣蚝瘮?shù)應(yīng)用的排序規(guī)則是考慮所有參數(shù)得來(lái)的,并且一個(gè)顯式的COLLATE子句將重載所有其他參數(shù)的排序規(guī)則(不過(guò),附加非匹配COLLATE子句到多于一個(gè)參數(shù)是一種錯(cuò)誤。詳見(jiàn) 第 23.2 節(jié))。因此,這會(huì)給出和前一個(gè)例子相同的結(jié)果:

SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';

但是這是一個(gè)錯(cuò)誤:

SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";

因?yàn)樗鼑L試把一個(gè)排序規(guī)則應(yīng)用到>操作符的結(jié)果,而它的數(shù)據(jù)類(lèi)型是非可排序數(shù)據(jù)類(lèi)型boolean。

4.2.11. 標(biāo)量子查詢(xún)

一個(gè)標(biāo)量子查詢(xún)是一種圓括號(hào)內(nèi)的普通SELECT查詢(xún),它剛好返回一行一列(關(guān)于書(shū)寫(xiě)查詢(xún)可見(jiàn)第 7 章)。SELECT查詢(xún)被執(zhí)行并且該單一返回值被使用在周?chē)闹当磉_(dá)式中。將一個(gè)返回超過(guò)一行或一列的查詢(xún)作為一個(gè)標(biāo)量子查詢(xún)使用是一種錯(cuò)誤(但是如果在一次特定執(zhí)行期間該子查詢(xún)沒(méi)有返回行則不是錯(cuò)誤,該標(biāo)量結(jié)果被當(dāng)做為空)。該子查詢(xún)可以從周?chē)牟樵?xún)中引用變量,這些變量在該子查詢(xún)的任何一次計(jì)算中都將作為常量。對(duì)于其他涉及子查詢(xún)的表達(dá)式還可見(jiàn) 第 9.23 節(jié)。

例如,下列語(yǔ)句會(huì)尋找每個(gè)州中最大的城市人口:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    FROM states;

4.2.12. 數(shù)組構(gòu)造器

一個(gè)數(shù)組構(gòu)造器是一個(gè)能構(gòu)建一個(gè)數(shù)組值并且將值用于它的成員元素的表達(dá)式。一個(gè)簡(jiǎn)單的數(shù)組構(gòu)造器由關(guān)鍵詞ARRAY、一個(gè)左方括號(hào)[、一個(gè)用于數(shù)組元素值的表達(dá)式列表(用逗號(hào)分隔)以及最后的一個(gè)右方括號(hào)]組成。例如:

SELECT ARRAY[1,2,3+4];
  array
---------
 {1,2,7}
(1 row)

默認(rèn)情況下,數(shù)組元素類(lèi)型是成員表達(dá)式的公共類(lèi)型,使用和UNIONCASE結(jié)構(gòu)(見(jiàn)第 10.5 節(jié))相同的規(guī)則決定。你可以通過(guò)顯式將數(shù)組構(gòu)造器造型為想要的類(lèi)型來(lái)重載,例如:

SELECT ARRAY[1,2,22.7]::integer[]; array ---------- {1,2,23} (1 row)

這和把每一個(gè)表達(dá)式單獨(dú)地造型為數(shù)組元素類(lèi)型的效果相同。關(guān)于造型的更多信息請(qǐng)見(jiàn)第 4.2.9 節(jié)。

多維數(shù)組值可以通過(guò)嵌套數(shù)組構(gòu)造器來(lái)構(gòu)建。在內(nèi)層的構(gòu)造器中,關(guān)鍵詞ARRAY可以被忽略。例如,這些語(yǔ)句產(chǎn)生相同的結(jié)果:

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)

因?yàn)槎嗑S數(shù)組必須是矩形的,處于同一層次的內(nèi)層構(gòu)造器必須產(chǎn)生相同維度的子數(shù)組。任何被應(yīng)用于外層ARRAY構(gòu)造器的造型會(huì)自動(dòng)傳播到所有的內(nèi)層構(gòu)造器。

多維數(shù)組構(gòu)造器元素可以是任何得到一個(gè)正確種類(lèi)數(shù)組的任何東西,而不僅僅是一個(gè)子-ARRAY結(jié)構(gòu)。例如:

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)

你可以構(gòu)造一個(gè)空數(shù)組,但是因?yàn)闊o(wú)法得到一個(gè)無(wú)類(lèi)型的數(shù)組,你必須顯式地把你的空數(shù)組造型成想要的類(lèi)型。例如:

SELECT ARRAY[]::integer[];
 array
-------
 {}
(1 row)

也可以從一個(gè)子查詢(xún)的結(jié)果構(gòu)建一個(gè)數(shù)組。在這種形式中,數(shù)組構(gòu)造器被寫(xiě)為關(guān)鍵詞ARRAY后跟著一個(gè)加了圓括號(hào)(不是方括號(hào))的子查詢(xún)。例如:

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)

子查詢(xún)必須返回一個(gè)單一列。如果子查詢(xún)的輸出列是非數(shù)組類(lèi)型, 結(jié)果的一維數(shù)組將為該子查詢(xún)結(jié)果中的每一行有一個(gè)元素, 并且有一個(gè)與子查詢(xún)的輸出列匹配的元素類(lèi)型。如果子查詢(xún)的輸出列 是一種數(shù)組類(lèi)型,結(jié)果將是同類(lèi)型的一個(gè)數(shù)組,但是要高一個(gè)維度。 在這種情況下,該子查詢(xún)的所有行必須產(chǎn)生同樣維度的數(shù)組,否則結(jié)果 就不會(huì)是矩形形式。

ARRAY構(gòu)建的一個(gè)數(shù)組值的下標(biāo)總是從一開(kāi)始。更多關(guān)于數(shù)組的信息,請(qǐng)見(jiàn)第 8.15 節(jié)。

4.2.13. 行構(gòu)造器

一個(gè)行構(gòu)造器是能夠構(gòu)建一個(gè)行值(也稱(chēng)作一個(gè)組合類(lèi)型)并用值作為其成員域的表達(dá)式。一個(gè)行構(gòu)造器由關(guān)鍵詞ROW、一個(gè)左圓括號(hào)、用于行的域值的零個(gè)或多個(gè)表達(dá)式(用逗號(hào)分隔)以及最后的一個(gè)右圓括號(hào)組成。例如:

SELECT ROW(1,2.5,'this is a test');

當(dāng)在列表中有超過(guò)一個(gè)表達(dá)式時(shí),關(guān)鍵詞ROW是可選的。

一個(gè)行構(gòu)造器可以包括語(yǔ)法rowvalue.*,它將被擴(kuò)展為該行值的元素的一個(gè)列表,就像在一個(gè)頂層SELECT列表(見(jiàn)第 8.16.5 節(jié))中使用 .*時(shí)發(fā)生的事情一樣。例如,如果表t有列f1f2,那么這些是相同的:

SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;

注意

PostgreSQL 8.2 以前,.*語(yǔ)法不會(huì)在行構(gòu)造器中被擴(kuò)展,這樣寫(xiě)ROW(t.*, 42)會(huì)創(chuàng)建一個(gè)有兩個(gè)域的行,其第一個(gè)域是另一個(gè)行值。新的行為通常更有用。如果你需要嵌套行值的舊行為,寫(xiě)內(nèi)層行值時(shí)不要用.*,例如ROW(t, 42)。

默認(rèn)情況下,由一個(gè)ROW表達(dá)式創(chuàng)建的值是一種匿名記錄類(lèi)型。如果必要,它可以被造型為一種命名的組合類(lèi)型 — 或者是一個(gè)表的行類(lèi)型,或者是一種用CREATE TYPE AS創(chuàng)建的組合類(lèi)型。為了避免歧義,可能需要一個(gè)顯式造型。例如:

CREATE TABLE mytable(f1 int, f2 float, f3 text);

CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- 不需要造型因?yàn)橹挥幸粋€(gè) getf1() 存在
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;

-- 現(xiàn)在我們需要一個(gè)造型來(lái)指示要調(diào)用哪個(gè)函數(shù):
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)

行構(gòu)造器可以被用來(lái)構(gòu)建存儲(chǔ)在一個(gè)組合類(lèi)型表列中的組合值,或者被傳遞給一個(gè)接受組合參數(shù)的函數(shù)。還有,可以比較兩個(gè)行值,或者用IS NULLIS NOT NULL測(cè)試一個(gè)行,例如:

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(table.*) IS NULL FROM table;  -- detect all-null rows

詳見(jiàn)第 9.24 節(jié)。如第 9.23 節(jié)中所討論的,行構(gòu)造器也可以被用來(lái)與子查詢(xún)相連接。

4.2.14. 表達(dá)式計(jì)算規(guī)則

子表達(dá)式的計(jì)算順序沒(méi)有被定義。特別地,一個(gè)操作符或函數(shù)的輸入不必按照從左至右或其他任何固定順序進(jìn)行計(jì)算。

此外,如果一個(gè)表達(dá)式的結(jié)果可以通過(guò)只計(jì)算其一部分來(lái)決定,那么其他子表達(dá)式可能完全不需要被計(jì)算。例如,如果我們寫(xiě):

SELECT true OR somefunc();

那么somefunc()將(可能)完全不被調(diào)用。如果我們寫(xiě)成下面這樣也是一樣:

SELECT somefunc() OR true;

注意這和一些編程語(yǔ)言中布爾操作符從左至右的短路不同。

因此,在復(fù)雜表達(dá)式中使用帶有副作用的函數(shù)是不明智的。在WHEREHAVING子句中依賴(lài)副作用或計(jì)算順序尤其危險(xiǎn),因?yàn)樵诮⒁粋€(gè)執(zhí)行計(jì)劃時(shí)這些子句會(huì)被廣泛地重新處理。這些子句中布爾表達(dá)式(AND/OR/NOT的組合)可能會(huì)以布爾代數(shù)定律所允許的任何方式被重組。

當(dāng)有必要強(qiáng)制計(jì)算順序時(shí),可以使用一個(gè)CASE結(jié)構(gòu)(見(jiàn)第 9.18 節(jié))。例如,在一個(gè)WHERE子句中使用下面的方法嘗試避免除零是不可靠的:

SELECT ... WHERE x > 0 AND y/x > 1.5;

但是這是安全的:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

一個(gè)以這種風(fēng)格使用的CASE結(jié)構(gòu)將使得優(yōu)化嘗試失敗,因此只有必要時(shí)才這樣做(在這個(gè)特別的例子中,最好通過(guò)寫(xiě)y > 1.5*x來(lái)回避這個(gè)問(wèn)題)。

不過(guò),CASE不是這類(lèi)問(wèn)題的萬(wàn)靈藥。上述技術(shù)的一個(gè)限制是, 它無(wú)法阻止常量子表達(dá)式的提早計(jì)算。如第 37.7 節(jié) 中所述,當(dāng)查詢(xún)被規(guī)劃而不是被執(zhí)行時(shí),被標(biāo)記成 IMMUTABLE的函數(shù)和操作符可以被計(jì)算。因此

SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;

很可能會(huì)導(dǎo)致一次除零失敗,因?yàn)橐?guī)劃器嘗試簡(jiǎn)化常量子表達(dá)式。即便是 表中的每一行都有x > 0(這樣運(yùn)行時(shí)永遠(yuǎn)不會(huì)進(jìn)入到 ELSE分支)也是這樣。

雖然這個(gè)特別的例子可能看起來(lái)愚蠢,沒(méi)有明顯涉及常量的情況可能會(huì)發(fā)生 在函數(shù)內(nèi)執(zhí)行的查詢(xún)中,因?yàn)橐驗(yàn)楹瘮?shù)參數(shù)的值和本地變量可以作為常量 被插入到查詢(xún)中用于規(guī)劃目的。例如,在PL/pgSQL函數(shù) 中,使用一個(gè)IF-THEN-ELSE語(yǔ)句來(lái) 保護(hù)一種有風(fēng)險(xiǎn)的計(jì)算比把它嵌在一個(gè) CASE表達(dá)式中要安全得多。

另一個(gè)同類(lèi)型的限制是,一個(gè)CASE無(wú)法阻止其所包含的聚集表達(dá)式 的計(jì)算,因?yàn)樵诳紤]SELECT列表或HAVING子句中的 其他表達(dá)式之前,會(huì)先計(jì)算聚集表達(dá)式。例如,下面的查詢(xún)會(huì)導(dǎo)致一個(gè)除零錯(cuò)誤, 雖然看起來(lái)好像已經(jīng)這種情況加以了保護(hù):

SELECT CASE WHEN min(employees) > 0
            THEN avg(expenses / employees)
       END
    FROM departments;

min()avg()聚集會(huì)在所有輸入行上并行地計(jì)算, 因此如果任何行有employees等于零,在有機(jī)會(huì)測(cè)試 min()的結(jié)果之前,就會(huì)發(fā)生除零錯(cuò)誤。取而代之的是,可以使用 一個(gè)WHEREFILTER子句來(lái)首先阻止有問(wèn)題的輸入行到達(dá) 一個(gè)聚集函數(shù)。


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)