PostgreSQL 索引的接口擴(kuò)展

2021-09-03 14:35 更新
37.16.1. 索引方法和操作符類
37.16.2. 索引方法策略
37.16.3. 索引方法支持例程
37.16.4. 一個(gè)例子
37.16.5. 操作符類和操作符族
37.16.6. 操作符類上的系統(tǒng)依賴
37.16.7. 排序操作符
37.16.8. 操作符類的特性

迄今為止已經(jīng)描述的過程讓我們能夠定義新的類型、新的函數(shù)以及新的操作符。但是,我們還不能在一種新數(shù)據(jù)類型的列上定義索引。要做這件事情,我們必須為新數(shù)據(jù)類型定義一個(gè)操作符類。在這一小節(jié)稍后的部分,我們將用一個(gè)例子闡述這部份內(nèi)容:一個(gè)用于 B-樹索引方法的操作符類,它以絕對值的升序存儲和排序復(fù)數(shù)。

操作符類可以被分組成操作符族來體現(xiàn)語義兼容的類之間的聯(lián)系。當(dāng)只涉及到一種單一數(shù)據(jù)類型時(shí),一個(gè)操作符類就足矣。因此我們將先把重點(diǎn)放在這種情況上,然后再回到操作符族。

37.16.1. 索引方法和操作符類

pg_am表為每一種索引方法都包含一行(內(nèi)部被稱為訪問方法)。PostgreSQL中內(nèi)建了對表常規(guī)訪問的支持,但是所有的索引方法則是在pg_am中描述??梢酝ㄟ^編寫必要的代碼并且在pg_am中創(chuàng)建一項(xiàng)來增加一種新的索引訪問方法 — 但這超出了本章的范圍(見 第 61 章)。

一個(gè)索引方法的例程并不直接了解它將要操作的數(shù)據(jù)類型。而是由一個(gè)操作符類 標(biāo)識索引方法用來操作一種特定數(shù)據(jù)類型的一組操作。之所以被稱為操作符類是因?yàn)樗鼈冎付ǖ囊患虑榫褪强梢员挥糜谝粋€(gè)索引的WHERE子句操作符集合(即,能被轉(zhuǎn)換成一個(gè)索引掃描條件)。一個(gè)操作符類也能指定一些索引方法內(nèi)部操作所需的支持函數(shù),這些過程不能直接對應(yīng)于能用于索引的任何WHERE子句操作符。

可以為相同的數(shù)據(jù)類型和索引方法定義多個(gè)操作符類。通過這種方式,可以為一種數(shù)據(jù)類型定義多個(gè)索引語義集合。例如,一個(gè)B-樹索引要求在它要操作的每一種數(shù)據(jù)類型上都定義一個(gè)排序順序。對一種復(fù)數(shù)數(shù)據(jù)類型來說,擁有一個(gè)可以根據(jù)復(fù)數(shù)絕對值排序的 B-樹操作符類和另一個(gè)可以根據(jù)實(shí)數(shù)部分排序的操作符類可能會有用。典型地,其中一個(gè)操作符類將被認(rèn)為是最常用的并且將被標(biāo)記為那種數(shù)據(jù)類型和索引方法的默認(rèn)操作符類。

相同的操作符類名稱可以被用于多個(gè)不同的索引方法(例如,B-樹和哈希索引方法都有名為int4_ops的操作符類)。但是每一個(gè)這樣的類都是一個(gè)獨(dú)立實(shí)體并且必須被單獨(dú)定義。

37.16.2. 索引方法策略

與一個(gè)操作符類關(guān)聯(lián)的操作符通過策略號標(biāo)識,它被用來標(biāo)識每個(gè)操作符在其操作符類中的語義。例如,B-樹在鍵上施行了一種嚴(yán)格的順序(較小到較大),因此小于大于等于這樣的操作符就是 B-樹所感興趣的。因?yàn)?span id="4co4xjl" class="productname">PostgreSQL允許用戶定義操作符,PostgreSQL不能看著一個(gè)操作符(如<>=)的名字并且說出它是哪一種比較。取而代之的是,索引方法定義了一個(gè)策略集合,它們可以被看成是廣義的操作符。每一個(gè)操作符類會說明對于一種特定的數(shù)據(jù)類型究竟是哪個(gè)實(shí)際的操作符對應(yīng)于每一種策略以及該索引語義的解釋。

B-樹索引方法定義了五種策略,如表 37.3所示。

表 37.3. B-樹策略

操作 策略號
小于 1
小于等于 2
等于 3
大于等于 4
大于 5

哈希索引只支持等值比較,因此它們只使用一種策略,如表 37.4所示。

表 37.4. 哈希策略

操作 策略號
等于 1

GiST 索引更加靈活:它們根本沒有一個(gè)固定的策略集合。取而代之的是,每一個(gè)特定 GiST 操作符類的consistency支持例程會負(fù)責(zé)解釋策略號。例如,一些內(nèi)建的 GiST 索引操作符類索引二維幾何對象,它們提供表 37.5中所示的 R-樹策略。其中四個(gè)是真正的二維測試(重疊、相同、包含、被包含),其中四個(gè)只考慮 X 方向,其他四個(gè)提供 Y 方向上的相同測試。

表 37.5. GiST 二維R-樹 策略

操作 策略號
左參數(shù)嚴(yán)格地位于右參數(shù)的左邊 1
左參數(shù)不會延伸到右參數(shù)的右邊 2
重疊 3
左參數(shù)不會延伸到右參數(shù)的左邊 4
左參數(shù)嚴(yán)格地位于右參數(shù)的右邊 5
相同 6
包含 7
被包含 8
不會延伸到高于 9
嚴(yán)格低于 10
嚴(yán)格高于 11
不會延伸到低于 12

SP-GiST 索引在靈活性上與索引相似:它們沒有一個(gè)固定的策略集合。取而代之的是,每一個(gè)操作符類的支持例程負(fù)責(zé)根據(jù)該操作符類的定義解釋策略號。例如,被內(nèi)建操作符類用于點(diǎn)的策略號如表 37.6中所示。

表 37.6. SP-GiST 點(diǎn)策略

操作 策略號
左參數(shù)嚴(yán)格地位于右參數(shù)的左邊 1
左參數(shù)嚴(yán)格地位于右參數(shù)的右邊 5
相同 6
被包含 8
嚴(yán)格地低于 10
嚴(yán)格地高于 11

GIN 索引與 GiST 和 SP-GiST 索引類似,它們也沒有一個(gè)固定的策略集合。取而代之的是,每一個(gè)操作符類的支持例程負(fù)責(zé)根據(jù)該操作符類的定義解釋策略號。例如,被內(nèi)建操作符類用于數(shù)組的策略號如表 37.7所示。

表 37.7. GIN 數(shù)組策略

操作 策略號
重疊 1
包含 2
被包含 3
等于 4

在沒有固定的策略集合這一點(diǎn)上,BRIN 索引和 GiST、SP-GiST 和 GIN 索引是類似的。每一個(gè)操作符類的支持函數(shù)會根據(jù)操作符類的定義解釋策略編號。例如,表 37.8中展示了內(nèi)建的Minmax操作符類所使用的策略編號。

表 37.8. BRIN 最小最大策略

操作 策略號
小于 1
小于等于 2
等于 3
大于等于 4
大于 5

注意上文列出的所有操作符都返回布爾值。實(shí)際上,所有作為索引方法搜索操作符定義的操作符必須返回類型boolean,因?yàn)樗鼈儽仨毘霈F(xiàn)在一個(gè)WHERE子句的頂層來與一個(gè)索引一起使用(某些索引訪問方法還支持排序操作符,它們通常不返回布爾值,這種特性在第 37.16.7 節(jié)中討論)。

37.16.3. 索引方法支持例程

對于系統(tǒng)來說只有策略信息通常不足以斷定如何使用一種索引。實(shí)際上,為了能工作,索引方法還要求額外的支持例程。例如,B-樹索引方法必須能比較兩個(gè)鍵并且決定其中一個(gè)是否大于、等于或小于另一個(gè)。類似地,哈希索引方法必須能夠?yàn)殒I值計(jì)算哈希碼。這些操作并不對應(yīng)在 SQL 命令的條件中使用的操作符。它們是索引方法在內(nèi)部使用的管理例程。

與策略一樣,操作符類會標(biāo)識哪些函數(shù)應(yīng)該為一種給定的數(shù)據(jù)類型扮演這些角色以及相應(yīng)的語義解釋。索引方法定義它需要的函數(shù)集合,而操作符類則會通過為函數(shù)分配由索引方法說明的支持函數(shù)號來標(biāo)識正確的函數(shù)。

此外,一些 opclass 允許用戶指定控制其行為的參數(shù)。每個(gè)內(nèi)置索引訪問方法都有一個(gè)可選的 options支持函數(shù),它定義了一組特定于 opclass 的參數(shù)。

如表 37.9所示, B-樹要求一個(gè)比較支持函數(shù),并且允許在操作符類作者的選項(xiàng)中提供四個(gè)額外的支持函數(shù)。這些支持函數(shù)的要求在第 63.3 節(jié)中會進(jìn)一步解釋。

表 37.9. B-樹支持函數(shù)

函數(shù) 支持號
比較兩個(gè)鍵并且返回一個(gè)小于零、等于零或大于零的整數(shù),它表示第一個(gè)鍵小于、等于或者大于第二個(gè)鍵。 1
返回C可調(diào)用的排序支持函數(shù)的地址(可選)。 2
將一個(gè)測試值與一個(gè)基礎(chǔ)值加上/減去一個(gè)偏移量的結(jié)果進(jìn)行比較,根據(jù)比較的結(jié)果返回真或假(可選) 3
確定使用運(yùn)算符類應(yīng)用 btree 重復(fù)數(shù)據(jù)刪除優(yōu)化的索引是否安全(可選) 4
定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 5

如表 37.10所示,哈希索引要求一個(gè)支持函數(shù),并且允許在操作符類作者的選項(xiàng)中提供兩個(gè)額外的支持函數(shù)。

表 37.10. 哈希支持函數(shù)

函數(shù) 支持號
為一個(gè)鍵計(jì)算32位哈希值 1
給定一個(gè)64-位salt,計(jì)算一個(gè)鍵的64位哈希值。如果salt為0,結(jié)果的低32位必須匹配會由函數(shù)1計(jì)算出來的值(可選) 2
定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 3

如表 37.11所示,GiST 索引有十個(gè)支持函數(shù),其中三個(gè)是可選的(詳見第 64 章)。

表 37.11. GiST 支持函數(shù)

函數(shù) 描述 支持號
consistent 判斷鍵是否滿足查詢修飾語 1
union 計(jì)算一個(gè)鍵集合的聯(lián)合 2
compress 計(jì)算一個(gè)要被索引的鍵或值的壓縮表達(dá) 3
decompress 計(jì)算一個(gè)壓縮鍵的解壓表達(dá) 4
penalty 計(jì)算把新鍵插入到帶有給定子樹鍵的子樹中帶來的罰值 5
picksplit 判斷一個(gè)頁面中的哪些項(xiàng)要被移動到新頁面中并且計(jì)算結(jié)果頁面的聯(lián)合鍵 6
equal 比較兩個(gè)鍵并且在它們相等時(shí)返回真 7
distance 判斷鍵到查詢值的距離(可選) 8
fetch 為只用索引掃描計(jì)算一個(gè)壓縮鍵的原始表達(dá)(可選) 9
options 定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 10

如表 37.12所示,SP-GiST 索引有六個(gè)支持函數(shù),其中一個(gè)是可選的(詳見第 65 章)。

表 37.12. SP-GiST 支持函數(shù)

函數(shù) 描述 支持號
config 提供有關(guān)該操作符類的基本信息 1
choose 判斷如何把一個(gè)新值插入到一個(gè)內(nèi)元組中 2
picksplit 判斷如何劃分一組值 3
inner_consistent 判斷對于一個(gè)查詢需要搜索哪一個(gè)子劃分 4
leaf_consistent 判斷鍵是否滿足查詢修飾語 5
options 定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 6

如表 37.13所示,GIN 索引有七個(gè)支持函數(shù),其中四個(gè)是可選的(詳見第 66 章)。

表 37.13. GIN 支持函數(shù)

函數(shù) 描述 支持號
compare 比較兩個(gè)鍵并且返回一個(gè)小于零、等于零或大于零的整數(shù),它表示第一個(gè)鍵小于、等于或者大于第二個(gè)鍵 1
extractValue 從一個(gè)要被索引的值中抽取鍵 2
extractQuery 從一個(gè)查詢條件中抽取鍵 3
consistent 判斷值是否匹配查詢條件(布爾變體)(如果支持函數(shù) 6 存在則是可選的) 4
comparePartial 比較來自查詢的部分鍵和來自索引的鍵,并且返回一個(gè)小于零、等于零或大于零的整數(shù),表示 GIN 是否應(yīng)該忽略該索引項(xiàng)、把該項(xiàng)當(dāng)做一個(gè)匹配或者停止索引掃描(可選) 5
triConsistent 判斷值是否匹配查詢條件(三元變體)(如果支持函數(shù) 4 存在則是可選的) 6
options 定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 7

表 37.14中所示,BRIN 索引具有五個(gè)基本的支持函數(shù),其中一個(gè)可選。 某些版本的基本功能需要提供額外的支持函數(shù)(更多信息請見第 67.3 節(jié))。

表 37.14. BRIN 支持函數(shù)

函數(shù) 描述 支持編號
opcInfo 返回描述被索引列的摘要數(shù)據(jù)的內(nèi)部信息 1
add_value 向一個(gè)現(xiàn)有的摘要索引元組增加一個(gè)新值 2
consistent 判斷值是否匹配查詢條件 3
union 計(jì)算兩個(gè)摘要元組的聯(lián)合 4
options 定義一組特定于此運(yùn)算符類的選項(xiàng)(可選) 5

和搜索操作符不同,支持函數(shù)返回特定索引方法所期望的數(shù)據(jù)類型,例如在 B 樹的比較函數(shù)中是一個(gè)有符號整數(shù)。每個(gè)支持函數(shù)的參數(shù)數(shù)量和類型也取決于索引方法。對于 B 樹和哈希,比較和哈希支持函數(shù)和包括在操作符類中的操作符接收一樣的輸入數(shù)據(jù)類型,但是大部分 GiST、SP-GiST、GIN 和 BRIN 支持函數(shù)則不是這樣。

37.16.4. 一個(gè)例子

現(xiàn)在我們已經(jīng)看過了基本思想,這里是創(chuàng)建一個(gè)新操作符類的例子(可以在源代碼的src/tutorial/complex.csrc/tutorial/complex.sql中找到這個(gè)例子)。該操作符類封裝了以絕對值順序排序復(fù)數(shù)的操作符,因此我們?yōu)樗∶麨?code class="literal">complex_abs_ops。首先,我們需要一個(gè)操作符集合。定義操作符的過程已經(jīng)在 第 37.14 節(jié)中討論過。對于一個(gè) B-樹上的操作符類,我們需要的操作符有:

  • 絕對值小于(策略 1)
  • 絕對值小于等于(策略 2)
  • 絕對值等于(策略 3)
  • 絕對值大于等于(策略 4)
  • 絕對值大于(策略 5)

定義一個(gè)比較操作符的相關(guān)集合最不容易出錯的方式是,先編寫 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);
}

其他四個(gè)函數(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)化器將無法有效地利用索引。

其他值得注意的事情:

  • 只能有一個(gè)操作符被命名為=且兩個(gè)操作數(shù)都為類型complex。在這種要求下,我們對于complex沒有任何其他操作符=。但是如果我們是在構(gòu)建一種實(shí)際的數(shù)據(jù)類型,我們可能想讓=成為復(fù)數(shù)的普通等值操作(不是絕對值的相等)。這樣,我們需要為 complex_abs_eq使用某種其他的操作符名稱。

  • 盡管PostgreSQL能夠處理具有相同 SQL 名稱的函數(shù)(只要它們具有不同的參數(shù)數(shù)據(jù)類型),但 C 只能處理具有給定名稱一個(gè)全局函數(shù)。因此,我們不能簡單地把 C 函數(shù)命名為abs_eq之類的東西。通常,在 C 函數(shù)名中包括數(shù)據(jù)類型的名稱是一種好習(xí)慣,這樣就不會與其他數(shù)據(jù)類型的函數(shù)發(fā)生沖突。

  • 我們可以讓函數(shù)也具有abs_eq這樣的 SQL 名稱,而依靠PostgreSQL通過參數(shù)數(shù)據(jù)類型來區(qū)分它和其他同名 SQL 函數(shù)。為了保持例子的簡潔,我們這里讓 C 級別和 SQL 級別的函數(shù)具有相同的名稱。

下一步是注冊 B-樹所要求的支持例程。實(shí)現(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)在應(yīng)該可以在complex列上創(chuàng)建并且使用 B-樹索引了。

我們可以把操作符項(xiàng)寫得更繁瑣,像這樣:

        OPERATOR        1       < (complex, complex) ,

但是當(dāng)操作符操作的數(shù)據(jù)類型和正在定義的操作符類所服務(wù)的數(shù)據(jù)類型相同時(shí)可以不用這么做。

上述例子假定這個(gè)新操作符類是complex數(shù)據(jù)類型的默認(rèn) B-樹操作符類。如果不是這樣,只需要省去關(guān)鍵詞DEFAULT。

37.16.5. 操作符類和操作符族

到目前為止,我們暗地里假設(shè)一個(gè)操作符類只處理一種數(shù)據(jù)類型。雖然在一個(gè)特定的索引列中必定只有一種數(shù)據(jù)類型,但是把被索引列與一種不同數(shù)據(jù)類型的值比較的索引操作通常也很有用。還有,如果與一種操作符類相關(guān)的擴(kuò)數(shù)據(jù)類型操作符有用,通常情況是其他數(shù)據(jù)類型也有其自身相關(guān)的操作符類。在相關(guān)的類之間建立起明確的聯(lián)系會很有用,因?yàn)檫@可以幫助規(guī)劃器進(jìn)行 SQL 查詢優(yōu)化(尤其是對于 B-樹操作符類,因?yàn)橐?guī)劃器包含了大量有關(guān)如何使用它們的知識)。

為了處理這些需求,PostgreSQL使用了操作符族的概念 。一個(gè)操作符族包含一個(gè)或者多個(gè)操作符類,并且也能包含屬于該族整體而不屬于該族中任何單一類的可索引操作符和相應(yīng)的支持函數(shù)。我們說這樣的操作符和函數(shù)是松散地存在于該族中,而不是被綁定在一個(gè)特定的類中。通常每個(gè)操作符類包含單一數(shù)據(jù)類型的操作符,而跨數(shù)據(jù)類型操作符則松散地存在于操作符族中。

一個(gè)操作符族中的所有操作符和函數(shù)必須具有兼容的語義,其中的兼容性要求由索引方法設(shè)定。你可能因此而奇怪為什么要這么麻煩地把族的特定子集單另出來成為操作符類,并且實(shí)際上(由于很多原因)這種劃分與操作符之間沒有什么直接的關(guān)聯(lián),只有操作符族才是實(shí)際的分組。定義操作符類的原因是,它們指定了特定索引對操作符族的依賴程度。如果一個(gè)索引使用著一個(gè)操作符類,那么不刪除該索引是不能刪除該操作符類的 — 但是操作符族的其他部分(即其他操作符類和松散的操作符)可以被刪除。因此,一個(gè)操作符類應(yīng)該包含一個(gè)索引在特定數(shù)據(jù)類型上正常工作所需要的最小操作符和函數(shù)集合,而相關(guān)但不關(guān)鍵的操作符可以作為操作符族的松散成員被加入。

例如,PostgreSQL有一個(gè)內(nèi)建的 B-樹操作符族integer_ops,它包括分別用于類型bigint (int8)、integer (int4)和smallint ( int2)列上索引的操作符類int8_ops、int4_ops以及int2_ops。這個(gè)族也包含跨數(shù)據(jù)類型比較操作符,它們允許對這些類型中的任意兩種進(jìn)行比較,這樣可以通過一種類型的比較值來搜索另一種類型之上的索引。這個(gè)族可以用這些定義來重現(xiàn):

CREATE OPERATOR FAMILY integer_ops USING btree;

CREATE OPERATOR CLASS int8_ops
DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
  -- 標(biāo)準(zhǔn) 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
  -- 標(biāo)準(zhǔn) 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
  -- 標(biāo)準(zhǔn) 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ù)號:每一個(gè)編號在該族中出現(xiàn)多次。只要一個(gè)特定編號的每一個(gè)實(shí)例都有可區(qū)分的輸入數(shù)據(jù)類型,就允許這樣做。輸入類型等于操作符類輸入類型的實(shí)例是該操作符類的主要操作符和支持函數(shù),并且在大部分情況下應(yīng)該被聲明為該操作符類的一部分而不是作為操作符族的松散成員存在。

第 63.2 節(jié)中的細(xì)節(jié)所述,在一個(gè) B-樹操作符族中,所有該族中的操作符必須以兼容的方式排序。對該族中的每一個(gè)操作符都必須有一個(gè)與該操作符具有相同的兩個(gè)輸入數(shù)據(jù)類型的支持函數(shù)。我們推薦讓操作符族保持完整,即對每一種數(shù)據(jù)類型的組合都應(yīng)該包括所有的操作符。每個(gè)操作符類只應(yīng)該包括非跨類型操作符和用于其數(shù)據(jù)類型的支持函數(shù)。

為了構(gòu)建一個(gè)多數(shù)據(jù)類型的哈希操作符族,必須為該族支持的每一種數(shù)據(jù)類型創(chuàng)建相兼容的哈希支持函數(shù)。這里的兼容性是指這些函數(shù)對于任意兩個(gè)被該族中等值操作符認(rèn)為相等的值會保證返回相同的哈希碼,即便這些值具有不同的類型時(shí)也是如此。當(dāng)這些類型具有不同的物理表示時(shí),這通常難以實(shí)現(xiàn),但是在某些情況下是可以做到的。此外,將該操作符族中一種數(shù)據(jù)類型的值通過隱式或者二進(jìn)制強(qiáng)制造型轉(zhuǎn)換成該族中另一種數(shù)據(jù)類型時(shí),不應(yīng)該改變所計(jì)算出的哈希值。注意每種數(shù)據(jù)類型只有一個(gè)支持函數(shù),而不是每個(gè)等值操作符一個(gè)。我們推薦讓操作符族保持完整,即對每一種數(shù)據(jù)類型的組合提供一個(gè)等值操作符。每個(gè)操作符類只應(yīng)該包括非跨類型等值操作符和用于其數(shù)據(jù)類型的支持函數(shù)。

GiST、SP-GiST 和 GIN 索引沒有任何明顯的跨數(shù)據(jù)類型操作的概念。它們所支持的操作符集合就是一個(gè)給定操作符類能夠處理的主要支持函數(shù)。

在 BRIN 中,需求取決于提供操作符類的框架。對于基于minmax的操作符類,必要的行為和 B-樹操作符族相同:族中的所有操作符必須以兼容的方式排序,并且轉(zhuǎn)換不能改變相關(guān)的排序順序。

注意

PostgreSQL 8.3 之前,沒有操作符族的概念,并且因此要在索引中使用的任何跨數(shù)據(jù)類型操作符必須被直接綁定到該索引的操作符類中。雖然這種方法仍然有效,但是已被廢棄,因?yàn)樗鼤屗饕囊蕾囘^于廣泛,還因?yàn)楫?dāng)兩種數(shù)據(jù)類型都在同一操作符族中有操作符時(shí)規(guī)劃器可以更有效地處理跨數(shù)據(jù)類型比較。

37.16.6. 操作符類上的系統(tǒng)依賴

PostgreSQL使用操作符類來以更多方式推斷操作符的屬性,而不僅僅是它們是否能被用于索引。因此,即便不準(zhǔn)備對你的數(shù)據(jù)類型的列建立索引,也可能想要創(chuàng)建操作符類。

特別地,ORDER BYDISTINCT等 SQL 特性要求對值的比較和排序。為了在用戶定義的數(shù)據(jù)類型上實(shí)現(xiàn)這些特性,PostgreSQL會為數(shù)據(jù)類型查找默認(rèn) B-樹操作符類。這個(gè)操作符類的equals成員定義了用于 GROUP BYDISTINCT的值的等值概念,而該操作符類施加的排序順序定義了默認(rèn)的ORDER BY順序。

如果一種數(shù)據(jù)類型沒有默認(rèn)的 B-樹操作符類,系統(tǒng)將查找默認(rèn)的哈希操作符類。但由于這類操作符類只提供等值,所以它只能支持分組而不能支持排序。

在一種數(shù)據(jù)類型沒有默認(rèn)操作符類時(shí),如果嘗試對該數(shù)據(jù)類型使用這些 SQL 特性,你將得到類似could not identify an ordering operator(無法標(biāo)識排序操作符)的錯誤。

注意

在版本 7.4 以前的PostgreSQL中,排序和分組操作將隱式地使用名為=、<以及>的操作符。新的依賴于默認(rèn)操作符類的行為避免了對具有特定名字的操作符行為作出任何假設(shè)。

通過在一個(gè)USING選項(xiàng)中指定一個(gè)非默認(rèn)B-樹操作符類的小于操作符,可以使用該操作符進(jìn)行排序,例如

SELECT * FROM mytable ORDER BY somecol USING ~<~;

或者,在USING中指定該操作符類的大于操作符可以選擇升序的排序。

用戶定義類型的數(shù)組的比較還依賴于該類型的默認(rèn)B-樹操作符類所定義的語義。如果沒有默認(rèn)的B-樹操作符類,但有一個(gè)默認(rèn)的哈希操作符類,則支持?jǐn)?shù)組的相等比較,但不支持順序的比較。

另一種要求更多數(shù)據(jù)類型相關(guān)知識的SQL特性是窗口函數(shù)(見第 4.2.8 節(jié))的RANGE offset PRECEDING/ FOLLOWING幀選項(xiàng)。對于這樣的一個(gè)查詢

SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
  FROM mytable;

不足以了解如何用x進(jìn)行排序,數(shù)據(jù)庫還必須理解如何對當(dāng)前行的x減5或者加10以標(biāo)識當(dāng)前窗口幀的邊界。把得到的邊界與其他行的x值用B-樹操作符類提供的比較操作符(定義了 ORDER BY順序)進(jìn)行比較是可能的 — 但是加和減操作符并不是該操作符類的一部分,因此應(yīng)該用哪些操作符呢?硬編碼的選擇是不切實(shí)際的,因?yàn)椴煌呐判蝽樞颍ú煌腂-樹操作符)可能需要不同的行為。因此,一個(gè)B-樹操作符類可以指定一個(gè)in_range支持函數(shù),它封裝有對排序順序有意義的加和減行為。如果有多種數(shù)據(jù)類型可以用作RANGE子句中的偏移量,甚至可以提供多個(gè)in_range支持函數(shù)。如果與窗口的 ORDER BY子句關(guān)聯(lián)的B-樹操作符類沒有一個(gè)匹配的in_range支持函數(shù),則不支持RANGE offset PRECEDING/FOLLOWING選項(xiàng)。

另一個(gè)要點(diǎn)是,出現(xiàn)在一個(gè)哈希操作符族中的操作符是哈希連接、哈希聚集和相關(guān)優(yōu)化的候選。這些情況下哈希操作符族就是至關(guān)重要的,因?yàn)樗鼧?biāo)識了要使用的哈希函數(shù)。

37.16.7. 排序操作符

有些索引訪問方法(當(dāng)前只有 GiST和SP-GiST)支持排序操作符的概念。到目前為止我們所討論的都是搜索操作符。搜索索引時(shí),會用搜索操作符來尋找所有滿足 WHERE indexed_column operator constant 的行。注意被返回的匹配行的順序是沒有任何保證的。相反,一個(gè)排序操作符并不限制能被返回的行集合,而是決定它們的順序。掃描索引時(shí),會使用排序操作符來以 ORDER BY indexed_column operator constant 所表示的順序返回行。這樣定義排序操作符的原因是,如果該操作符能度量距離,它就能支持最近鄰搜索。例如,這樣的一個(gè)查詢

SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;

尋找離一個(gè)給定目標(biāo)點(diǎn)最近的十個(gè)位置。位置列上的 GiST 索引可以有效地完成這個(gè)查詢,因?yàn)?code class="literal"><->是一個(gè)排序操作符。

搜索操作符必須返回布爾結(jié)果,排序操作符通常返回某種其他類型,例如浮點(diǎn)、數(shù)字或者距離。這種類型通常不同于被索引的數(shù)據(jù)類型。為了避免硬編碼有關(guān)不同數(shù)據(jù)類型行為的假設(shè),需要定義一個(gè)排序操作符來提名一個(gè) B-樹操作符族指定結(jié)果數(shù)據(jù)類型的排序順序。正如我們在前一節(jié)介紹的,B-樹操作符族定義了PostgreSQL的順序概念,因此這是一種自然的表達(dá)。由于點(diǎn)<->操作符返回 float8,可以在一個(gè)操作符類創(chuàng)建命令中這樣指定它:

OPERATOR 15    <-> (point, point) FOR ORDER BY float_ops

其中float_ops是包括float8上操作的內(nèi)建操作符族。這種聲明說明該索引能夠以<->操作符的遞增值順序返回行。

37.16.8. 操作符類的特性

有兩個(gè)操作符類的特性我們還沒有討論,主要是因?yàn)樗鼈儗τ谧畛S玫乃饕椒ú惶杏谩?/p>

通常,把一個(gè)操作符聲明為一個(gè)操作符類(或操作符族)的成員意味著該索引方法能夠使用該操作符準(zhǔn)確地檢索滿足WHERE條件的行集。例如:

SELECT * FROM table WHERE integer_column < 4;

恰好可以被該整數(shù)列上一個(gè) B-樹索引滿足。但是也有情況下索引只是作為匹配行的非精確向?qū)А@?,如果一個(gè) GiST 索引只存儲幾何對象的邊界框,那么它無法精確地滿足測試非矩形對象(如多邊形)之間相交的WHERE條件。但是我們可以使用該索引來尋找邊界框與目標(biāo)對象的邊界框相交的對象,并且只在通過該索引找到的對象上做精確的相交測試。如果適用于這種場景,該索引被稱為對該操作符是有損的。有損索引搜索通過在一行可能滿足或者不滿足該查詢條件時(shí)返回一個(gè) recheck標(biāo)志來實(shí)現(xiàn)。核心系統(tǒng)將接著在檢索到的行上測試原始查詢條件來看它是否應(yīng)該被作為一個(gè)合法匹配返回。如果索引被保證能返回所有所需的行外加一些額外的行,這種方法就能有效,因?yàn)槟切╊~外的行可以通過執(zhí)行原始的操作符調(diào)用來消除。支持有損搜索的索引方法(當(dāng)前有 GiST、SP-GiST 和 GIN)允許個(gè)別操作符類的支持函數(shù)設(shè)置 recheck 標(biāo)志,因此這也是一種操作符類的重要特性。

再次考慮在索引中只存儲復(fù)雜對象(如多邊形)的邊界框的情況。在這種情況下,把整個(gè)多邊形存儲在索引項(xiàng)中沒有很大價(jià)值 — 我們也可以只存儲一個(gè)更簡單的box類型對象。這種情況通過CREATE OPERATOR CLASS中的STORAGE選項(xiàng)表示:

CREATE OPERATOR CLASS polygon_ops
    DEFAULT FOR TYPE polygon USING gist AS
        ...
        STORAGE box;

當(dāng)前,只有 GiST、GIN 和 BRIN 索引方法支持不同于列數(shù)據(jù)類型的STORAGE類型。在使用STORAGE時(shí),GiST 的支持例程compressdecompress必須處理數(shù)據(jù)類型轉(zhuǎn)換。在 GIN 中,STORAGE類型標(biāo)識 key值的類型,它通常不同于被索引列的類型 — 例如,一個(gè)用于整數(shù)數(shù)組列的操作符類可能具有整數(shù)鍵值。GIN 的支持例程extractValueextractQuery負(fù)責(zé)從被索引值中抽取鍵。BRIN 類似于 GIN:STORAGE類型標(biāo)識被存儲的摘要值的類型,而操作符類的支持過程負(fù)責(zé)正確解釋摘要值。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號