W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
迄今為止已經(jīng)描述的過程讓我們能夠定義新的類型、新的函數(shù)以及新的操作符。但是,我們還不能在一種新數(shù)據(jù)類型的列上定義索引。要做這件事情,我們必須為新數(shù)據(jù)類型定義一個操作符類。在這一小節(jié)稍后的部分,我們將用一個例子闡述這部份內(nèi)容:一個用于 B-樹索引方法的操作符類,它以絕對值的升序存儲和排序復數(shù)。
操作符類可以被分組成操作符族來體現(xiàn)語義兼容的類之間的聯(lián)系。當只涉及到一種單一數(shù)據(jù)類型時,一個操作符類就足矣。因此我們將先把重點放在這種情況上,然后再回到操作符族。
pg_am
表為每一種索引方法都包含一行(內(nèi)部被稱為訪問方法)。PostgreSQL中內(nèi)建了對表常規(guī)訪問的支持,但是所有的索引方法則是在pg_am
中描述??梢酝ㄟ^編寫必要的代碼并且在pg_am
中創(chuàng)建一項來增加一種新的索引訪問方法 — 但這超出了本章的范圍(見
第 61 章)。
一個索引方法的例程并不直接了解它將要操作的數(shù)據(jù)類型。而是由一個操作符類
標識索引方法用來操作一種特定數(shù)據(jù)類型的一組操作。之所以被稱為操作符類是因為它們指定的一件事情就是可以被用于一個索引的WHERE
子句操作符集合(即,能被轉(zhuǎn)換成一個索引掃描條件)。一個操作符類也能指定一些索引方法內(nèi)部操作所需的支持函數(shù),這些過程不能直接對應于能用于索引的任何WHERE
子句操作符。
可以為相同的數(shù)據(jù)類型和索引方法定義多個操作符類。通過這種方式,可以為一種數(shù)據(jù)類型定義多個索引語義集合。例如,一個B-樹索引要求在它要操作的每一種數(shù)據(jù)類型上都定義一個排序順序。對一種復數(shù)數(shù)據(jù)類型來說,擁有一個可以根據(jù)復數(shù)絕對值排序的 B-樹操作符類和另一個可以根據(jù)實數(shù)部分排序的操作符類可能會有用。典型地,其中一個操作符類將被認為是最常用的并且將被標記為那種數(shù)據(jù)類型和索引方法的默認操作符類。
相同的操作符類名稱可以被用于多個不同的索引方法(例如,B-樹和哈希索引方法都有名為int4_ops
的操作符類)。但是每一個這樣的類都是一個獨立實體并且必須被單獨定義。
與一個操作符類關聯(lián)的操作符通過“策略號”標識,它被用來標識每個操作符在其操作符類中的語義。例如,B-樹在鍵上施行了一種嚴格的順序(較小到較大),因此“小于”和“大于等于”這樣的操作符就是
B-樹所感興趣的。因為PostgreSQL允許用戶定義操作符,PostgreSQL不能看著一個操作符(如<
和>=
)的名字并且說出它是哪一種比較。取而代之的是,索引方法定義了一個“策略”集合,它們可以被看成是廣義的操作符。每一個操作符類會說明對于一種特定的數(shù)據(jù)類型究竟是哪個實際的操作符對應于每一種策略以及該索引語義的解釋。
B-樹索引方法定義了五種策略,如表 37.3所示。
表 37.3. B-樹策略
操作 | 策略號 |
---|---|
小于 | 1 |
小于等于 | 2 |
等于 | 3 |
大于等于 | 4 |
大于 | 5 |
哈希索引只支持等值比較,因此它們只使用一種策略,如表 37.4所示。
表 37.4. 哈希策略
操作 | 策略號 |
---|---|
等于 | 1 |
GiST 索引更加靈活:它們根本沒有一個固定的策略集合。取而代之的是,每一個特定 GiST 操作符類的“consistency”支持例程會負責解釋策略號。例如,一些內(nèi)建的 GiST 索引操作符類索引二維幾何對象,它們提供表 37.5中所示的 “R-樹”策略。其中四個是真正的二維測試(重疊、相同、包含、被包含),其中四個只考慮 X 方向,其他四個提供 Y 方向上的相同測試。
表 37.5. GiST 二維“R-樹” 策略
操作 | 策略號 |
---|---|
左參數(shù)嚴格地位于右參數(shù)的左邊 | 1 |
左參數(shù)不會延伸到右參數(shù)的右邊 | 2 |
重疊 | 3 |
左參數(shù)不會延伸到右參數(shù)的左邊 | 4 |
左參數(shù)嚴格地位于右參數(shù)的右邊 | 5 |
相同 | 6 |
包含 | 7 |
被包含 | 8 |
不會延伸到高于 | 9 |
嚴格低于 | 10 |
嚴格高于 | 11 |
不會延伸到低于 | 12 |
SP-GiST 索引在靈活性上與索引相似:它們沒有一個固定的策略集合。取而代之的是,每一個操作符類的支持例程負責根據(jù)該操作符類的定義解釋策略號。例如,被內(nèi)建操作符類用于點的策略號如表 37.6中所示。
表 37.6. SP-GiST 點策略
操作 | 策略號 |
---|---|
左參數(shù)嚴格地位于右參數(shù)的左邊 | 1 |
左參數(shù)嚴格地位于右參數(shù)的右邊 | 5 |
相同 | 6 |
被包含 | 8 |
嚴格地低于 | 10 |
嚴格地高于 | 11 |
GIN 索引與 GiST 和 SP-GiST 索引類似,它們也沒有一個固定的策略集合。取而代之的是,每一個操作符類的支持例程負責根據(jù)該操作符類的定義解釋策略號。例如,被內(nèi)建操作符類用于數(shù)組的策略號如表 37.7所示。
表 37.7. GIN 數(shù)組策略
操作 | 策略號 |
---|---|
重疊 | 1 |
包含 | 2 |
被包含 | 3 |
等于 | 4 |
在沒有固定的策略集合這一點上,BRIN 索引和 GiST、SP-GiST 和 GIN 索引是類似的。每一個操作符類的支持函數(shù)會根據(jù)操作符類的定義解釋策略編號。例如,表 37.8中展示了內(nèi)建的Minmax
操作符類所使用的策略編號。
表 37.8. BRIN 最小最大策略
操作 | 策略號 |
---|---|
小于 | 1 |
小于等于 | 2 |
等于 | 3 |
大于等于 | 4 |
大于 | 5 |
注意上文列出的所有操作符都返回布爾值。實際上,所有作為索引方法搜索操作符定義的操作符必須返回類型boolean
,因為它們必須出現(xiàn)在一個WHERE
子句的頂層來與一個索引一起使用(某些索引訪問方法還支持排序操作符,它們通常不返回布爾值,這種特性在第 37.16.7 節(jié)中討論)。
對于系統(tǒng)來說只有策略信息通常不足以斷定如何使用一種索引。實際上,為了能工作,索引方法還要求額外的支持例程。例如,B-樹索引方法必須能比較兩個鍵并且決定其中一個是否大于、等于或小于另一個。類似地,哈希索引方法必須能夠為鍵值計算哈希碼。這些操作并不對應在 SQL 命令的條件中使用的操作符。它們是索引方法在內(nèi)部使用的管理例程。
與策略一樣,操作符類會標識哪些函數(shù)應該為一種給定的數(shù)據(jù)類型扮演這些角色以及相應的語義解釋。索引方法定義它需要的函數(shù)集合,而操作符類則會通過為函數(shù)分配由索引方法說明的“支持函數(shù)號”來標識正確的函數(shù)。
此外,一些 opclass 允許用戶指定控制其行為的參數(shù)。每個內(nèi)置索引訪問方法都有一個可選的 options
支持函數(shù),它定義了一組特定于 opclass 的參數(shù)。
如表 37.9所示, B-樹要求一個比較支持函數(shù),并且允許在操作符類作者的選項中提供四個額外的支持函數(shù)。這些支持函數(shù)的要求在第 63.3 節(jié)中會進一步解釋。
表 37.9. B-樹支持函數(shù)
函數(shù) | 支持號 |
---|---|
比較兩個鍵并且返回一個小于零、等于零或大于零的整數(shù),它表示第一個鍵小于、等于或者大于第二個鍵。 | 1 |
返回C可調(diào)用的排序支持函數(shù)的地址(可選)。 | 2 |
將一個測試值與一個基礎值加上/減去一個偏移量的結(jié)果進行比較,根據(jù)比較的結(jié)果返回真或假(可選) | 3 |
確定使用運算符類應用 btree 重復數(shù)據(jù)刪除優(yōu)化的索引是否安全(可選) | 4 |
定義一組特定于此運算符類的選項(可選) | 5 |
如表 37.10所示,哈希索引要求一個支持函數(shù),并且允許在操作符類作者的選項中提供兩個額外的支持函數(shù)。
表 37.10. 哈希支持函數(shù)
函數(shù) | 支持號 |
---|---|
為一個鍵計算32位哈希值 | 1 |
給定一個64-位salt,計算一個鍵的64位哈希值。如果salt為0,結(jié)果的低32位必須匹配會由函數(shù)1計算出來的值(可選) | 2 |
定義一組特定于此運算符類的選項(可選) | 3 |
如表 37.11所示,GiST 索引有十個支持函數(shù),其中三個是可選的(詳見第 64 章)。
表 37.11. GiST 支持函數(shù)
函數(shù) | 描述 | 支持號 |
---|---|---|
consistent
|
判斷鍵是否滿足查詢修飾語 | 1 |
union
|
計算一個鍵集合的聯(lián)合 | 2 |
compress
|
計算一個要被索引的鍵或值的壓縮表達 | 3 |
decompress
|
計算一個壓縮鍵的解壓表達 | 4 |
penalty
|
計算把新鍵插入到帶有給定子樹鍵的子樹中帶來的罰值 | 5 |
picksplit
|
判斷一個頁面中的哪些項要被移動到新頁面中并且計算結(jié)果頁面的聯(lián)合鍵 | 6 |
equal
|
比較兩個鍵并且在它們相等時返回真 | 7 |
distance
|
判斷鍵到查詢值的距離(可選) | 8 |
fetch
|
為只用索引掃描計算一個壓縮鍵的原始表達(可選) | 9 |
options
|
定義一組特定于此運算符類的選項(可選) | 10 |
如表 37.12所示,SP-GiST 索引有六個支持函數(shù),其中一個是可選的(詳見第 65 章)。
表 37.12. SP-GiST 支持函數(shù)
函數(shù) | 描述 | 支持號 |
---|---|---|
config
|
提供有關該操作符類的基本信息 | 1 |
choose
|
判斷如何把一個新值插入到一個內(nèi)元組中 | 2 |
picksplit
|
判斷如何劃分一組值 | 3 |
inner_consistent
|
判斷對于一個查詢需要搜索哪一個子劃分 | 4 |
leaf_consistent
|
判斷鍵是否滿足查詢修飾語 | 5 |
options
|
定義一組特定于此運算符類的選項(可選) | 6 |
如表 37.13所示,GIN 索引有七個支持函數(shù),其中四個是可選的(詳見第 66 章)。
表 37.13. GIN 支持函數(shù)
函數(shù) | 描述 | 支持號 |
---|---|---|
compare
|
比較兩個鍵并且返回一個小于零、等于零或大于零的整數(shù),它表示第一個鍵小于、等于或者大于第二個鍵 | 1 |
extractValue
|
從一個要被索引的值中抽取鍵 | 2 |
extractQuery
|
從一個查詢條件中抽取鍵 | 3 |
consistent
|
判斷值是否匹配查詢條件(布爾變體)(如果支持函數(shù) 6 存在則是可選的) | 4 |
comparePartial
|
比較來自查詢的部分鍵和來自索引的鍵,并且返回一個小于零、等于零或大于零的整數(shù),表示 GIN 是否應該忽略該索引項、把該項當做一個匹配或者停止索引掃描(可選) | 5 |
triConsistent
|
判斷值是否匹配查詢條件(三元變體)(如果支持函數(shù) 4 存在則是可選的) | 6 |
options
|
定義一組特定于此運算符類的選項(可選) | 7 |
如表 37.14中所示,BRIN 索引具有五個基本的支持函數(shù),其中一個可選。 某些版本的基本功能需要提供額外的支持函數(shù)(更多信息請見第 67.3 節(jié))。
表 37.14. BRIN 支持函數(shù)
函數(shù) | 描述 | 支持編號 |
---|---|---|
opcInfo
|
返回描述被索引列的摘要數(shù)據(jù)的內(nèi)部信息 | 1 |
add_value
|
向一個現(xiàn)有的摘要索引元組增加一個新值 | 2 |
consistent
|
判斷值是否匹配查詢條件 | 3 |
union
|
計算兩個摘要元組的聯(lián)合 | 4 |
options
|
定義一組特定于此運算符類的選項(可選) | 5 |
和搜索操作符不同,支持函數(shù)返回特定索引方法所期望的數(shù)據(jù)類型,例如在 B 樹的比較函數(shù)中是一個有符號整數(shù)。每個支持函數(shù)的參數(shù)數(shù)量和類型也取決于索引方法。對于 B 樹和哈希,比較和哈希支持函數(shù)和包括在操作符類中的操作符接收一樣的輸入數(shù)據(jù)類型,但是大部分 GiST、SP-GiST、GIN 和 BRIN 支持函數(shù)則不是這樣。
現(xiàn)在我們已經(jīng)看過了基本思想,這里是創(chuàng)建一個新操作符類的例子(可以在源代碼的src/tutorial/complex.c
和src/tutorial/complex.sql
中找到這個例子)。該操作符類封裝了以絕對值順序排序復數(shù)的操作符,因此我們?yōu)樗∶麨?code class="literal">complex_abs_ops。首先,我們需要一個操作符集合。定義操作符的過程已經(jīng)在
第 37.14 節(jié)中討論過。對于一個 B-樹上的操作符類,我們需要的操作符有:
定義一個比較操作符的相關集合最不容易出錯的方式是,先編寫 B-樹比較支持函數(shù),然后編寫該支持函數(shù)的包裝器函數(shù)。這降低了極端情況下得到不一致結(jié)果的幾率。遵照這種方法,我們首先編寫:
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
static int
complex_abs_cmp_internal(Complex *a, Complex *b)
{
double amag = Mag(a),
bmag = Mag(b);
if (amag < bmag)
return -1;
if (amag > bmag)
return 1;
return 0;
}
現(xiàn)在小于函數(shù)看起來像這樣:
PG_FUNCTION_INFO_V1(complex_abs_lt);
Datum
complex_abs_lt(PG_FUNCTION_ARGS)
{
Complex *a = (Complex *) PG_GETARG_POINTER(0);
Complex *b = (Complex *) PG_GETARG_POINTER(1);
PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}
其他四個函數(shù)的區(qū)別只在于它們?nèi)绾伪容^內(nèi)部函數(shù)的結(jié)果與 0。
接下來我們基于這些函數(shù)聲明 SQL 的函數(shù)和操作符:
CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
AS 'filename
', 'complex_abs_lt'
LANGUAGE C IMMUTABLE STRICT;
CREATE OPERATOR < (
leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
commutator = > , negator = >= ,
restrict = scalarltsel, join = scalarltjoinsel
);
指定正確的交換子和求反器操作符很重要,合適的限制和連接選擇度函數(shù)也是一樣,否則優(yōu)化器將無法有效地利用索引。
其他值得注意的事情:
只能有一個操作符被命名為=
且兩個操作數(shù)都為類型complex
。在這種要求下,我們對于complex
沒有任何其他操作符=
。但是如果我們是在構建一種實際的數(shù)據(jù)類型,我們可能想讓=
成為復數(shù)的普通等值操作(不是絕對值的相等)。這樣,我們需要為
complex_abs_eq
使用某種其他的操作符名稱。
盡管PostgreSQL能夠處理具有相同 SQL 名稱的函數(shù)(只要它們具有不同的參數(shù)數(shù)據(jù)類型),但 C 只能處理具有給定名稱一個全局函數(shù)。因此,我們不能簡單地把 C 函數(shù)命名為abs_eq
之類的東西。通常,在 C 函數(shù)名中包括數(shù)據(jù)類型的名稱是一種好習慣,這樣就不會與其他數(shù)據(jù)類型的函數(shù)發(fā)生沖突。
我們可以讓函數(shù)也具有abs_eq
這樣的 SQL 名稱,而依靠PostgreSQL通過參數(shù)數(shù)據(jù)類型來區(qū)分它和其他同名 SQL 函數(shù)。為了保持例子的簡潔,我們這里讓 C 級別和 SQL 級別的函數(shù)具有相同的名稱。
下一步是注冊 B-樹所要求的支持例程。實現(xiàn)支持例程的 C 代碼例子在包含操作符函數(shù)的同一文件中。我們這樣來聲明該函數(shù):
CREATE FUNCTION complex_abs_cmp(complex, complex)
RETURNS integer
AS 'filename
'
LANGUAGE C IMMUTABLE STRICT;
現(xiàn)在我們已經(jīng)有了所需的操作符和支持例程,就可以最終創(chuàng)建操作符類:
CREATE OPERATOR CLASS complex_abs_ops
DEFAULT FOR TYPE complex USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 complex_abs_cmp(complex, complex);
做好了!現(xiàn)在應該可以在complex
列上創(chuàng)建并且使用 B-樹索引了。
我們可以把操作符項寫得更繁瑣,像這樣:
OPERATOR 1 < (complex, complex) ,
但是當操作符操作的數(shù)據(jù)類型和正在定義的操作符類所服務的數(shù)據(jù)類型相同時可以不用這么做。
上述例子假定這個新操作符類是complex
數(shù)據(jù)類型的默認 B-樹操作符類。如果不是這樣,只需要省去關鍵詞DEFAULT
。
到目前為止,我們暗地里假設一個操作符類只處理一種數(shù)據(jù)類型。雖然在一個特定的索引列中必定只有一種數(shù)據(jù)類型,但是把被索引列與一種不同數(shù)據(jù)類型的值比較的索引操作通常也很有用。還有,如果與一種操作符類相關的擴數(shù)據(jù)類型操作符有用,通常情況是其他數(shù)據(jù)類型也有其自身相關的操作符類。在相關的類之間建立起明確的聯(lián)系會很有用,因為這可以幫助規(guī)劃器進行 SQL 查詢優(yōu)化(尤其是對于 B-樹操作符類,因為規(guī)劃器包含了大量有關如何使用它們的知識)。
為了處理這些需求,PostgreSQL使用了操作符族的概念 。一個操作符族包含一個或者多個操作符類,并且也能包含屬于該族整體而不屬于該族中任何單一類的可索引操作符和相應的支持函數(shù)。我們說這樣的操作符和函數(shù)是“松散地”存在于該族中,而不是被綁定在一個特定的類中。通常每個操作符類包含單一數(shù)據(jù)類型的操作符,而跨數(shù)據(jù)類型操作符則松散地存在于操作符族中。
一個操作符族中的所有操作符和函數(shù)必須具有兼容的語義,其中的兼容性要求由索引方法設定。你可能因此而奇怪為什么要這么麻煩地把族的特定子集單另出來成為操作符類,并且實際上(由于很多原因)這種劃分與操作符之間沒有什么直接的關聯(lián),只有操作符族才是實際的分組。定義操作符類的原因是,它們指定了特定索引對操作符族的依賴程度。如果一個索引使用著一個操作符類,那么不刪除該索引是不能刪除該操作符類的 — 但是操作符族的其他部分(即其他操作符類和松散的操作符)可以被刪除。因此,一個操作符類應該包含一個索引在特定數(shù)據(jù)類型上正常工作所需要的最小操作符和函數(shù)集合,而相關但不關鍵的操作符可以作為操作符族的松散成員被加入。
例如,PostgreSQL有一個內(nèi)建的 B-樹操作符族integer_ops
,它包括分別用于類型bigint
(int8
)、integer
(int4
)和smallint
(
int2
)列上索引的操作符類int8_ops
、int4_ops
以及int2_ops
。這個族也包含跨數(shù)據(jù)類型比較操作符,它們允許對這些類型中的任意兩種進行比較,這樣可以通過一種類型的比較值來搜索另一種類型之上的索引。這個族可以用這些定義來重現(xiàn):
CREATE OPERATOR FAMILY integer_ops USING btree;
CREATE OPERATOR CLASS int8_ops
DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
-- 標準 int8 比較
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 btint8cmp(int8, int8) ,
FUNCTION 2 btint8sortsupport(internal) ,
FUNCTION 3 in_range(int8, int8, int8, boolean, boolean) ,
FUNCTION 4 btequalimage(oid) ;
CREATE OPERATOR CLASS int4_ops
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
-- 標準 int4 比較
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 btint4cmp(int4, int4) ,
FUNCTION 2 btint4sortsupport(internal) ,
FUNCTION 3 in_range(int4, int4, int4, boolean, boolean) ,
FUNCTION 4 btequalimage(oid) ;
CREATE OPERATOR CLASS int2_ops
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
-- 標準 int2 比較
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 btint2cmp(int2, int2) ,
FUNCTION 2 btint2sortsupport(internal) ,
FUNCTION 3 in_range(int2, int2, int2, boolean, boolean) ,
FUNCTION 4 btequalimage(oid) ;
ALTER OPERATOR FAMILY integer_ops USING btree ADD
-- 跨類型比較 int8 vs int2
OPERATOR 1 < (int8, int2) ,
OPERATOR 2 <= (int8, int2) ,
OPERATOR 3 = (int8, int2) ,
OPERATOR 4 >= (int8, int2) ,
OPERATOR 5 > (int8, int2) ,
FUNCTION 1 btint82cmp(int8, int2) ,
-- 跨類型比較 int8 vs int4
OPERATOR 1 < (int8, int4) ,
OPERATOR 2 <= (int8, int4) ,
OPERATOR 3 = (int8, int4) ,
OPERATOR 4 >= (int8, int4) ,
OPERATOR 5 > (int8, int4) ,
FUNCTION 1 btint84cmp(int8, int4) ,
-- 跨類型比較 int4 vs int2
OPERATOR 1 < (int4, int2) ,
OPERATOR 2 <= (int4, int2) ,
OPERATOR 3 = (int4, int2) ,
OPERATOR 4 >= (int4, int2) ,
OPERATOR 5 > (int4, int2) ,
FUNCTION 1 btint42cmp(int4, int2) ,
-- 跨類型比較 int4 vs int8
OPERATOR 1 < (int4, int8) ,
OPERATOR 2 <= (int4, int8) ,
OPERATOR 3 = (int4, int8) ,
OPERATOR 4 >= (int4, int8) ,
OPERATOR 5 > (int4, int8) ,
FUNCTION 1 btint48cmp(int4, int8) ,
-- 跨類型比較 int2 vs int8
OPERATOR 1 < (int2, int8) ,
OPERATOR 2 <= (int2, int8) ,
OPERATOR 3 = (int2, int8) ,
OPERATOR 4 >= (int2, int8) ,
OPERATOR 5 > (int2, int8) ,
FUNCTION 1 btint28cmp(int2, int8) ,
-- 跨類型比較 int2 vs int4
OPERATOR 1 < (int2, int4) ,
OPERATOR 2 <= (int2, int4) ,
OPERATOR 3 = (int2, int4) ,
OPERATOR 4 >= (int2, int4) ,
OPERATOR 5 > (int2, int4) ,
FUNCTION 1 btint24cmp(int2, int4) ,
-- 跨類型的in_range函數(shù)
FUNCTION 3 in_range(int4, int4, int8, boolean, boolean) ,
FUNCTION 3 in_range(int4, int4, int2, boolean, boolean) ,
FUNCTION 3 in_range(int2, int2, int8, boolean, boolean) ,
FUNCTION 3 in_range(int2, int2, int4, boolean, boolean) ;
注意這種定義“重載”了操作符策略和支持函數(shù)號:每一個編號在該族中出現(xiàn)多次。只要一個特定編號的每一個實例都有可區(qū)分的輸入數(shù)據(jù)類型,就允許這樣做。輸入類型等于操作符類輸入類型的實例是該操作符類的主要操作符和支持函數(shù),并且在大部分情況下應該被聲明為該操作符類的一部分而不是作為操作符族的松散成員存在。
如第 63.2 節(jié)中的細節(jié)所述,在一個 B-樹操作符族中,所有該族中的操作符必須以兼容的方式排序。對該族中的每一個操作符都必須有一個與該操作符具有相同的兩個輸入數(shù)據(jù)類型的支持函數(shù)。我們推薦讓操作符族保持完整,即對每一種數(shù)據(jù)類型的組合都應該包括所有的操作符。每個操作符類只應該包括非跨類型操作符和用于其數(shù)據(jù)類型的支持函數(shù)。
為了構建一個多數(shù)據(jù)類型的哈希操作符族,必須為該族支持的每一種數(shù)據(jù)類型創(chuàng)建相兼容的哈希支持函數(shù)。這里的兼容性是指這些函數(shù)對于任意兩個被該族中等值操作符認為相等的值會保證返回相同的哈希碼,即便這些值具有不同的類型時也是如此。當這些類型具有不同的物理表示時,這通常難以實現(xiàn),但是在某些情況下是可以做到的。此外,將該操作符族中一種數(shù)據(jù)類型的值通過隱式或者二進制強制造型轉(zhuǎn)換成該族中另一種數(shù)據(jù)類型時,不應該改變所計算出的哈希值。注意每種數(shù)據(jù)類型只有一個支持函數(shù),而不是每個等值操作符一個。我們推薦讓操作符族保持完整,即對每一種數(shù)據(jù)類型的組合提供一個等值操作符。每個操作符類只應該包括非跨類型等值操作符和用于其數(shù)據(jù)類型的支持函數(shù)。
GiST、SP-GiST 和 GIN 索引沒有任何明顯的跨數(shù)據(jù)類型操作的概念。它們所支持的操作符集合就是一個給定操作符類能夠處理的主要支持函數(shù)。
在 BRIN 中,需求取決于提供操作符類的框架。對于基于minmax
的操作符類,必要的行為和 B-樹操作符族相同:族中的所有操作符必須以兼容的方式排序,并且轉(zhuǎn)換不能改變相關的排序順序。
在PostgreSQL 8.3 之前,沒有操作符族的概念,并且因此要在索引中使用的任何跨數(shù)據(jù)類型操作符必須被直接綁定到該索引的操作符類中。雖然這種方法仍然有效,但是已被廢棄,因為它會讓索引的依賴過于廣泛,還因為當兩種數(shù)據(jù)類型都在同一操作符族中有操作符時規(guī)劃器可以更有效地處理跨數(shù)據(jù)類型比較。
PostgreSQL使用操作符類來以更多方式推斷操作符的屬性,而不僅僅是它們是否能被用于索引。因此,即便不準備對你的數(shù)據(jù)類型的列建立索引,也可能想要創(chuàng)建操作符類。
特別地,ORDER BY
和DISTINCT
等 SQL 特性要求對值的比較和排序。為了在用戶定義的數(shù)據(jù)類型上實現(xiàn)這些特性,PostgreSQL會為數(shù)據(jù)類型查找默認 B-樹操作符類。這個操作符類的“equals”成員定義了用于
GROUP BY
和DISTINCT
的值的等值概念,而該操作符類施加的排序順序定義了默認的ORDER BY
順序。
如果一種數(shù)據(jù)類型沒有默認的 B-樹操作符類,系統(tǒng)將查找默認的哈希操作符類。但由于這類操作符類只提供等值,所以它只能支持分組而不能支持排序。
在一種數(shù)據(jù)類型沒有默認操作符類時,如果嘗試對該數(shù)據(jù)類型使用這些 SQL 特性,你將得到類似“could not identify an ordering operator”(無法標識排序操作符)的錯誤。
在版本 7.4 以前的PostgreSQL中,排序和分組操作將隱式地使用名為=
、<
以及>
的操作符。新的依賴于默認操作符類的行為避免了對具有特定名字的操作符行為作出任何假設。
通過在一個USING
選項中指定一個非默認B-樹操作符類的小于操作符,可以使用該操作符進行排序,例如
SELECT * FROM mytable ORDER BY somecol USING ~<~;
或者,在USING
中指定該操作符類的大于操作符可以選擇升序的排序。
用戶定義類型的數(shù)組的比較還依賴于該類型的默認B-樹操作符類所定義的語義。如果沒有默認的B-樹操作符類,但有一個默認的哈希操作符類,則支持數(shù)組的相等比較,但不支持順序的比較。
另一種要求更多數(shù)據(jù)類型相關知識的SQL特性是窗口函數(shù)(見第 4.2.8 節(jié))的RANGE
offset
PRECEDING
/
FOLLOWING
幀選項。對于這樣的一個查詢
SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
FROM mytable;
不足以了解如何用x
進行排序,數(shù)據(jù)庫還必須理解如何對當前行的x
值“減5”或者“加10”以標識當前窗口幀的邊界。把得到的邊界與其他行的x
值用B-樹操作符類提供的比較操作符(定義了
ORDER BY
順序)進行比較是可能的 — 但是加和減操作符并不是該操作符類的一部分,因此應該用哪些操作符呢?硬編碼的選擇是不切實際的,因為不同的排序順序(不同的B-樹操作符)可能需要不同的行為。因此,一個B-樹操作符類可以指定一個in_range支持函數(shù),它封裝有對排序順序有意義的加和減行為。如果有多種數(shù)據(jù)類型可以用作RANGE
子句中的偏移量,甚至可以提供多個in_range支持函數(shù)。如果與窗口的
ORDER BY
子句關聯(lián)的B-樹操作符類沒有一個匹配的in_range支持函數(shù),則不支持RANGE
offset
PRECEDING
/FOLLOWING
選項。
另一個要點是,出現(xiàn)在一個哈希操作符族中的操作符是哈希連接、哈希聚集和相關優(yōu)化的候選。這些情況下哈希操作符族就是至關重要的,因為它標識了要使用的哈希函數(shù)。
有些索引訪問方法(當前只有 GiST和SP-GiST)支持排序操作符的概念。到目前為止我們所討論的都是搜索操作符。搜索索引時,會用搜索操作符來尋找所有滿足 WHERE
indexed_column
operator
constant
的行。注意被返回的匹配行的順序是沒有任何保證的。相反,一個排序操作符并不限制能被返回的行集合,而是決定它們的順序。掃描索引時,會使用排序操作符來以 ORDER BY
indexed_column
operator
constant
所表示的順序返回行。這樣定義排序操作符的原因是,如果該操作符能度量距離,它就能支持最近鄰搜索。例如,這樣的一個查詢
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
尋找離一個給定目標點最近的十個位置。位置列上的 GiST 索引可以有效地完成這個查詢,因為<->
是一個排序操作符。
搜索操作符必須返回布爾結(jié)果,排序操作符通常返回某種其他類型,例如浮點、數(shù)字或者距離。這種類型通常不同于被索引的數(shù)據(jù)類型。為了避免硬編碼有關不同數(shù)據(jù)類型行為的假設,需要定義一個排序操作符來提名一個 B-樹操作符族指定結(jié)果數(shù)據(jù)類型的排序順序。正如我們在前一節(jié)介紹的,B-樹操作符族定義了PostgreSQL的順序概念,因此這是一種自然的表達。由于點<->
操作符返回
float8
,可以在一個操作符類創(chuàng)建命令中這樣指定它:
OPERATOR 15 <-> (point, point) FOR ORDER BY float_ops
其中float_ops
是包括float8
上操作的內(nèi)建操作符族。這種聲明說明該索引能夠以<->
操作符的遞增值順序返回行。
有兩個操作符類的特性我們還沒有討論,主要是因為它們對于最常用的索引方法不太有用。
通常,把一個操作符聲明為一個操作符類(或操作符族)的成員意味著該索引方法能夠使用該操作符準確地檢索滿足WHERE
條件的行集。例如:
SELECT * FROM table WHERE integer_column < 4;
恰好可以被該整數(shù)列上一個 B-樹索引滿足。但是也有情況下索引只是作為匹配行的非精確向?qū)?。例如,如果一個 GiST 索引只存儲幾何對象的邊界框,那么它無法精確地滿足測試非矩形對象(如多邊形)之間相交的WHERE
條件。但是我們可以使用該索引來尋找邊界框與目標對象的邊界框相交的對象,并且只在通過該索引找到的對象上做精確的相交測試。如果適用于這種場景,該索引被稱為對該操作符是“有損的”。有損索引搜索通過在一行可能滿足或者不滿足該查詢條件時返回一個
recheck標志來實現(xiàn)。核心系統(tǒng)將接著在檢索到的行上測試原始查詢條件來看它是否應該被作為一個合法匹配返回。如果索引被保證能返回所有所需的行外加一些額外的行,這種方法就能有效,因為那些額外的行可以通過執(zhí)行原始的操作符調(diào)用來消除。支持有損搜索的索引方法(當前有 GiST、SP-GiST 和 GIN)允許個別操作符類的支持函數(shù)設置 recheck 標志,因此這也是一種操作符類的重要特性。
再次考慮在索引中只存儲復雜對象(如多邊形)的邊界框的情況。在這種情況下,把整個多邊形存儲在索引項中沒有很大價值 — 我們也可以只存儲一個更簡單的box
類型對象。這種情況通過CREATE OPERATOR CLASS
中的STORAGE
選項表示:
CREATE OPERATOR CLASS polygon_ops
DEFAULT FOR TYPE polygon USING gist AS
...
STORAGE box;
當前,只有 GiST、GIN 和 BRIN 索引方法支持不同于列數(shù)據(jù)類型的STORAGE
類型。在使用STORAGE
時,GiST 的支持例程compress
和decompress
必須處理數(shù)據(jù)類型轉(zhuǎn)換。在 GIN 中,STORAGE
類型標識
“key”值的類型,它通常不同于被索引列的類型 — 例如,一個用于整數(shù)數(shù)組列的操作符類可能具有整數(shù)鍵值。GIN 的支持例程extractValue
和extractQuery
負責從被索引值中抽取鍵。BRIN 類似于 GIN:STORAGE
類型標識被存儲的摘要值的類型,而操作符類的支持過程負責正確解釋摘要值。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: