PostgreSQL 排序規(guī)則支持

2021-08-31 15:55 更新
23.2.1. 概念
23.2.2. 管理排序規(guī)則

排序規(guī)則特性允許指定每一列甚至每一個(gè)操作的數(shù)據(jù)的排序順序和字符分類行為。這放松了數(shù)據(jù)庫的LC_COLLATELC_CTYPE設(shè)置自創(chuàng)建以后就不能更改這一限制。

23.2.1. 概念

在概念上,一種可排序數(shù)據(jù)類型的每一種表達(dá)式都有一個(gè)排序規(guī)則(內(nèi)建的可排序數(shù)據(jù)類型是text、varcharchar。用戶定義的基礎(chǔ)類型也可以被標(biāo)記為可排序的,并且在一種可排序數(shù)據(jù)類型上的域也是可排序的)。如果該表達(dá)式是一個(gè)列引用,該表達(dá)式的排序規(guī)則就是列所定義的排序規(guī)則。如果該表達(dá)式是一個(gè)常量,排序規(guī)則就是該常量數(shù)據(jù)類型的默認(rèn)排序規(guī)則。更復(fù)雜表達(dá)式的排序規(guī)則根據(jù)其輸入的排序規(guī)則得來,如下所述:

一個(gè)表達(dá)式的排序規(guī)則可以是默認(rèn)排序規(guī)則,它表示數(shù)據(jù)庫的區(qū)域設(shè)置。一個(gè)表達(dá)式的排序規(guī)則也可能是不確定的。在這種情況下,排序操作和其他需要知道排序規(guī)則的操作會(huì)失敗。

當(dāng)數(shù)據(jù)庫系統(tǒng)必須要執(zhí)行一次排序或者字符分類時(shí),它使用輸入表達(dá)式的排序規(guī)則。這會(huì)在使用例如ORDER BY子句以及函數(shù)或操作符調(diào)用(如<)時(shí)發(fā)生。應(yīng)用于ORDER BY子句的排序規(guī)則就是排序鍵的排序規(guī)則。應(yīng)用于函數(shù)或操作符調(diào)用的排序規(guī)則從它們的參數(shù)得來,具體如下文所述。除比較操作符之外,在大小寫字母之間轉(zhuǎn)換的函數(shù)會(huì)考慮排序規(guī)則,例如 lower、upperinitcap。模式匹配操作符和to_char及相關(guān)函數(shù)也會(huì)考慮排序規(guī)則。

對(duì)于一個(gè)函數(shù)或操作符調(diào)用,其排序規(guī)則通過檢查在執(zhí)行指定操作時(shí)參數(shù)的排序規(guī)則來獲得。如果該函數(shù)或操作符調(diào)用的結(jié)果是一種可排序的數(shù)據(jù)類型,萬一有外圍表達(dá)式要求函數(shù)或操作符表達(dá)式的排序規(guī)則,在解析時(shí)結(jié)果的排序規(guī)則也會(huì)被用作函數(shù)或操作符表達(dá)式的排序規(guī)則。

一個(gè)表達(dá)式的排序規(guī)則派生可以是顯式或隱式。該區(qū)別會(huì)影響多個(gè)不同的排序規(guī)則出現(xiàn)在同一個(gè)表達(dá)式中時(shí)如何組合它們。當(dāng)使用一個(gè)COLLATE子句時(shí),將發(fā)生顯式排序規(guī)則派生。所有其他排序規(guī)則派生都是隱式的。當(dāng)多個(gè)排序規(guī)則需要被組合時(shí)(例如在一個(gè)函數(shù)調(diào)用中),將使用下面的規(guī)則:

  1. 如果任何一個(gè)輸入表達(dá)式具有一個(gè)顯式排序規(guī)則派生,則在輸入表達(dá)式之間的所有顯式派生的排序規(guī)則必須相同,否則將產(chǎn)生一個(gè)錯(cuò)誤。如果任何一個(gè)顯式派生的排序規(guī)則存在,它就是排序規(guī)則組合的結(jié)果。

  2. 否則,所有輸入表達(dá)式必須具有相同的隱式排序規(guī)則派生或默認(rèn)排序規(guī)則。如果任何一個(gè)非默認(rèn)排序規(guī)則存在,它就是排序規(guī)則組合的結(jié)果。否則,結(jié)果是默認(rèn)排序規(guī)則。

  3. 如果在輸入表達(dá)式之間存在沖突的非默認(rèn)隱式排序規(guī)則,則組合被認(rèn)為是具有不確定排序規(guī)則。這并非一種錯(cuò)誤情況,除非被調(diào)用的特定函數(shù)要求提供排序規(guī)則的知識(shí)。如果它確實(shí)這樣做,運(yùn)行時(shí)將發(fā)生一個(gè)錯(cuò)誤。

例如,考慮這個(gè)表定義:

CREATE TABLE test1 (
    a text COLLATE "de_DE",
    b text COLLATE "es_ES",
    ...
);

然后在

SELECT a < 'foo' FROM test1;

中,<比較被根據(jù)de_DE規(guī)則執(zhí)行,因?yàn)楸磉_(dá)式組合了一個(gè)隱式派生的排序規(guī)則和默認(rèn)排序規(guī)則。但是在

SELECT a < ('foo' COLLATE "fr_FR") FROM test1;

中,比較被使用fr_FR規(guī)則執(zhí)行,因?yàn)轱@式排序規(guī)則派生重載了隱式排序規(guī)則。更進(jìn)一步,給定

SELECT a < b FROM test1;

解析器不能確定要應(yīng)用哪個(gè)排序規(guī)則,因?yàn)?code class="structfield">a列和b列具有沖突的隱式排序規(guī)則。由于<操作符不需要知道到底使用哪一個(gè)排序規(guī)則,這將會(huì)導(dǎo)致一個(gè)錯(cuò)誤。該錯(cuò)誤可以通過在一個(gè)輸入表達(dá)式上附加一個(gè)顯式排序規(guī)則說明符來解決,因此:

SELECT a < b COLLATE "de_DE" FROM test1;

或者等效的

SELECT a COLLATE "de_DE" < b FROM test1;

在另一方面,結(jié)構(gòu)相似的情況

SELECT a || b FROM test1;

不會(huì)導(dǎo)致一個(gè)錯(cuò)誤,因?yàn)?code class="literal">||操作符不關(guān)心排序規(guī)則:不管排序規(guī)則怎樣它的結(jié)果都相同。

如果一個(gè)函數(shù)或操作符發(fā)送一個(gè)具有可排序數(shù)據(jù)類型的結(jié)果,分配給該函數(shù)或操作符的組合輸入表達(dá)式的排序規(guī)則也被考慮應(yīng)用在函數(shù)或操作符的結(jié)果。因此,在

SELECT * FROM test1 ORDER BY a || 'foo';

中排序?qū)⒏鶕?jù)de_DE規(guī)則完成。但這個(gè)查詢:

SELECT * FROM test1 ORDER BY a || b;

會(huì)導(dǎo)致一個(gè)錯(cuò)誤,因?yàn)榧词?code class="literal">||操作符不需要知道排序規(guī)則,但ORDER BY子句需要。按照以前,沖突可以通過使用一個(gè)顯式排序規(guī)則說明符來解決:

SELECT * FROM test1 ORDER BY a || b COLLATE "fr_FR";

23.2.2. 管理排序規(guī)則

排序規(guī)則是SQL模式對(duì)象,它將SQL名稱映射到操作系統(tǒng)中安裝的庫提供的語言環(huán)境。 排序規(guī)則定義中有一個(gè)提供程序, 它指定哪個(gè)庫提供語言環(huán)境數(shù)據(jù)。一個(gè)標(biāo)準(zhǔn)的提供者名稱是libc, 它使用操作系統(tǒng)C庫提供的語言環(huán)境。這些是操作系統(tǒng)提供的大多數(shù)工具使用的語言環(huán)境。 另一個(gè)提供者是icu,它使用外部ICU 庫。 只有在構(gòu)建PostgreSQL時(shí)配置了對(duì)ICU的支持,才能使用ICU區(qū)域設(shè)置。

libc提供的一個(gè)排序規(guī)則對(duì)象映射到LC_COLLATELC_CTYPE設(shè)置的組合, 如setlocale()系統(tǒng)庫調(diào)用所接受的。 (正如其名字所說的,一個(gè)排序規(guī)則的主要目的是設(shè)置LC_COLLATE, 它控制排序順序。但是在實(shí)際中 LC_CTYPE設(shè)置與LC_COLLATE 不同是很少有必要的,因此通過一個(gè)概念來收集這些信息比為了設(shè)置每一個(gè)表達(dá)式的 LC_CTYPE而創(chuàng)建另一種架構(gòu)要更加方便)。此外, 一個(gè)libc排序規(guī)則是和一個(gè)字符集編碼(見第 23.3 節(jié)) 綁定在一起的。相同的排序規(guī)則名字可能存在于不同的編碼中。

icu提供的排序規(guī)則對(duì)象映射到由ICU庫提供的指定整理器。 ICU不支持單獨(dú)的collatectype設(shè)置, 所以它們總是相同的。此外,ICU排序規(guī)則與編碼無關(guān), 因此在數(shù)據(jù)庫中總是只有一個(gè)給定名稱的ICU排序規(guī)則。

23.2.2.1. 標(biāo)準(zhǔn)的排序規(guī)則

在所有的平臺(tái)上,名為default、CPOSIX的排序規(guī)則都可用。附加的排序規(guī)則是否可用取決于操作系統(tǒng)的支持。default排序規(guī)則選擇在數(shù)據(jù)庫創(chuàng)建時(shí)指定的LC_COLLATELC_CTYPE值。 CPOSIX排序規(guī)則都指定了傳統(tǒng)的C行為,在其中只有ASCII字母AZ被視為字母,并且排序嚴(yán)格地按照字符編碼的字節(jié)值完成。

此外,SQL標(biāo)準(zhǔn)排序規(guī)則名稱ucs_basic可用于編碼UTF8。 它相當(dāng)于C,并按Unicode代碼點(diǎn)排序。

23.2.2.2. 預(yù)定義的排序規(guī)則

如果操作系統(tǒng)支持在一個(gè)程序中使用多個(gè)區(qū)域(newlocale和相關(guān)函數(shù)), 或者配置了ICU支持,那么在一個(gè)數(shù)據(jù)集簇被初始化時(shí),initdb 將以它在操作系統(tǒng)中能找到的所有區(qū)域?yàn)榛A(chǔ)在系統(tǒng)目錄pg_collation 中填充排序規(guī)則。

要檢查當(dāng)前可用的語言環(huán)境,請(qǐng)?jiān)?span id="mekokes" class="application">psql中使用查詢 SELECT * FROM pg_collation或命令\dOS +。

23.2.2.2.1. libc 排序規(guī)則

例如,操作系統(tǒng)可能會(huì)提供一個(gè)名為de_DE.utf8的區(qū)域。initdb則會(huì)創(chuàng)建一個(gè)用于編碼UTF8的名為de_DE.utf8的排序規(guī)則,在其中LC_COLLATELC_CTYPE都被設(shè)置為 de_DE.utf8。它也會(huì)創(chuàng)建一個(gè)具有去掉名稱的.utf8標(biāo)簽的排序規(guī)則。這樣你也可以使用名字de_DE來使用該排序規(guī)則,這寫起來更簡(jiǎn)單并且使得名字更加獨(dú)立于編碼。不過要注意,最初的排序規(guī)則名稱的集合是平臺(tái)依賴的。

libc提供的默認(rèn)排序規(guī)則直接映射到操作系統(tǒng)中安裝的語言環(huán)境, 可以使用命令locale -a列出。如果所需的libc 排序規(guī)則與LC_COLLATELC_CTYPE的值不同, 或者在數(shù)據(jù)庫系統(tǒng)初始化之后, 操作系統(tǒng)中安裝了新的語言環(huán)境,可以使用 CREATE COLLATION 命令創(chuàng)建新的排序規(guī)則。新的操作系統(tǒng)語言環(huán)境也可以使用 pg_import_system_collations() 函數(shù)集中導(dǎo)入。

在任何特定的數(shù)據(jù)庫中,只有使用數(shù)據(jù)庫編碼的排序規(guī)則是令人感興趣的。其他pg_collation中的項(xiàng)會(huì)被忽略。因此,一個(gè)如de_DE的被剝離的排序規(guī)則名在一個(gè)給定數(shù)據(jù)庫中可以被認(rèn)為是唯一的,即使它在全局上并不唯一。我們推薦使用被剝離的排序規(guī)則名,因?yàn)樵谀銢Q定要更改到另一個(gè)數(shù)據(jù)庫編碼時(shí)需要做的事情更少。但是要注意default、 CPOSIX排序規(guī)則在使用時(shí)可以不考慮數(shù)據(jù)庫編碼。

PostgreSQL在碰到具有相同屬性的不同排序規(guī)則對(duì)象時(shí)會(huì)認(rèn)為它們是不兼容的。因此對(duì)于例子:

SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1;

將會(huì)得到一個(gè)錯(cuò)誤,即使CPOSIX排序規(guī)則具有相同的行為。因此,我們不推薦混合使用被剝離的和非被剝離的排序規(guī)則名。

23.2.2.2.2. ICU 排序規(guī)則

對(duì)于ICU,枚舉所有可能的語言環(huán)境名稱并不明智。 ICU為語言環(huán)境使用特定的命名系統(tǒng),但命名語言環(huán)境的方法多于實(shí)際上不同的語言環(huán)境。 initdb使用ICU API提取一組不同的語言環(huán)境以填充初始排序規(guī)則集合。 由ICU提供的排序規(guī)則是在SQL環(huán)境中創(chuàng)建的,名稱采用BCP 47語言標(biāo)記格式, 并附有一個(gè)專用擴(kuò)展名-x-icu, 以將它們與libc語言環(huán)境區(qū)分開來。

以下是可能創(chuàng)建的一些排序規(guī)則的示例:

de-x-icu

德語排序規(guī)則,默認(rèn)變體

de-AT-x-icu

奧地利的德語排序規(guī)則,默認(rèn)變體

(也就是說de-DE-x-icude-CH-x-icu,但是這種寫法,相當(dāng)于 de-x-icu。)

und-x-icu (for undefined)

ICU root排序規(guī)則。 使用它獲取合理的語言無關(guān)的排序順序

一些(不常用的)編碼不受ICU支持。當(dāng)數(shù)據(jù)庫編碼是其中之一時(shí), 忽略pg_collation中的ICU排序規(guī)則項(xiàng)。 試圖使用其中一個(gè)將會(huì)拋出一個(gè)類似collation "de-x-icu" for encoding "WIN874" does not exist的錯(cuò)誤。

23.2.2.3. 創(chuàng)建新的排序規(guī)則對(duì)象

如果標(biāo)準(zhǔn)和預(yù)定義的排序規(guī)則不夠用,用戶可以使用SQL命令 CREATE COLLATION創(chuàng)建自己的排序規(guī)則對(duì)象。

與所有預(yù)定于的對(duì)象一樣,標(biāo)準(zhǔn)和預(yù)定義的排序規(guī)則在模式 pg_catalog中。用戶定義的排序規(guī)則應(yīng)該在用戶模式中創(chuàng)建。 這也確保它們由pg_dump保存。

23.2.2.3.1. libc 排序規(guī)則

可以像這樣創(chuàng)建新的libc排序規(guī)則:

CREATE COLLATION german (provider = libc, locale = 'de_DE');

該命令中locale子句可接受的確切值取決于操作系統(tǒng)。 在類Unix系統(tǒng)上,命令locale -a將顯示一個(gè)列表。

由于預(yù)定義的libc排序規(guī)則已經(jīng)包含了數(shù)據(jù)庫實(shí)例初始化時(shí)在操作系統(tǒng)中定義的所有排序規(guī)則, 因此通常不需要手動(dòng)創(chuàng)建新排序規(guī)則。如果需要不同的命名系統(tǒng)(在這種情況下, 另請(qǐng)參閱第 23.2.2.3.3 節(jié)), 或者操作系統(tǒng)已經(jīng)升級(jí)以提供新的區(qū)域設(shè)置定義(在這種情況下, 另請(qǐng)參閱pg_import_system_collations()), 可能需要手動(dòng)創(chuàng)建。

23.2.2.3.2. ICU 排序規(guī)則

ICU允許自定義超出由initdb 預(yù)加載的基本語言+國(guó)家/地區(qū)集的排序規(guī)則。鼓勵(lì)用戶定義他們自己的排序規(guī)則對(duì)象, 利用這些條件來滿足他們排序行為的需求。請(qǐng)參閱 http://userguide.icu-project.org/localehttp://userguide.icu-project.org/collation/api 獲取有關(guān)ICU區(qū)域設(shè)置命名的信息??山邮艿拿Q和屬性集取決于特定的ICU版本。

這里有些例子:

CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk');
CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de@collation=phonebook');

德語排序規(guī)則和電話簿排序規(guī)則類型

第一個(gè)例子使用語言標(biāo)簽根據(jù) BCP 47選擇了ICU區(qū)域設(shè)置。 第二個(gè)示例使用傳統(tǒng)的ICU特定區(qū)域設(shè)置語法。第一種風(fēng)格是首選, 但它不受舊版ICU支持。

請(qǐng)注意,您可以在SQL環(huán)境中任意指定排序規(guī)則對(duì)象的名稱。 在這個(gè)例子中,我們遵循預(yù)定義排序規(guī)則使用的命名風(fēng)格, 而這種風(fēng)格又遵循BCP 47,但這對(duì)于用戶定義的排序規(guī)則不是必需的。

CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji');
CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = '@collation=emoji');

根據(jù)Unicode技術(shù)標(biāo)準(zhǔn)#51,使用表情符號(hào)排序規(guī)則類型的根排序規(guī)則

觀察傳統(tǒng)ICU區(qū)域命名系統(tǒng)中的方式,根區(qū)域設(shè)置由空字符串選擇。

CREATE COLLATION latinlast (provider = icu, locale = 'en-u-kr-grek-latn');
CREATE COLLATION latinlast (provider = icu, locale = 'en@colReorder=grek-latn');

在拉丁字母之前對(duì)希臘字母進(jìn)行排序。(默認(rèn)為拉丁語在希臘語之前。)

CREATE COLLATION upperfirst (provider = icu, locale = 'en-u-kf-upper');
CREATE COLLATION upperfirst (provider = icu, locale = 'en@colCaseFirst=upper');

在小寫字母前面排列大寫字母。(默認(rèn)是小寫字母在前面。)

CREATE COLLATION special (provider = icu, locale = 'en-u-kf-upper-kr-grek-latn');
CREATE COLLATION special (provider = icu, locale = 'en@colCaseFirst=upper;colReorder=grek-latn');

結(jié)合上述兩個(gè)選項(xiàng)。

CREATE COLLATION numeric (provider = icu, locale = 'en-u-kn-true');
CREATE COLLATION numeric (provider = icu, locale = 'en@colNumeric=yes');

數(shù)字排序,按數(shù)字值排序數(shù)字序列,例如: A-21 < A-123(也稱為自然排序)。

參閱Unicode 技術(shù)標(biāo)準(zhǔn) #35BCP 47獲取詳細(xì)信息。 可能的排序規(guī)則類型(co子標(biāo)簽)列表可以在 CLDR 倉庫中找到。 區(qū)域設(shè)置瀏覽器 可以用于檢查一個(gè)特定區(qū)域設(shè)置定義的細(xì)節(jié)。使用k* 子標(biāo)簽的示例至少要求ICU版本54。

請(qǐng)注意,雖然此系統(tǒng)允許創(chuàng)建忽略大小寫忽略重音符或類似(使用ks鍵)的排序規(guī)則,但為了使這些排序規(guī)則真正以大小寫敏感或重音敏感方式工作,它們也需要在CREATE COLLATION中定義為非 確定性的;見本文章中 第 23.2.2.4 節(jié)。否則,根據(jù)排序規(guī)則比較相等但按照字節(jié)不相等的任何字符串將根據(jù)其字節(jié)值進(jìn)行排序。

注意

根據(jù)設(shè)計(jì),ICU幾乎可以接受任何字符串作為區(qū)域名稱, 并使用其文檔中描述的后備程序?qū)⑵渑c最接近的區(qū)域設(shè)置相匹配。因此, 如果使用給定ICU安裝實(shí)際上不支持的功能組合排序規(guī)范,則不會(huì)有直接反饋。 因此建議創(chuàng)建應(yīng)用程序級(jí)別的測(cè)試用例,以檢查排序規(guī)則定義是否滿足需求。

23.2.2.3.3. 復(fù)制排序規(guī)則

也可以使用命令CREATE COLLATION 從現(xiàn)有的排序規(guī)則創(chuàng)建新的排序規(guī)則, 這對(duì)于能夠在應(yīng)用程序中使用與操作系統(tǒng)無關(guān)的排序規(guī)則名稱、 創(chuàng)建兼容性名稱或以更易讀的名稱使用ICU提供的排序規(guī)則很有幫助。例如:

CREATE COLLATION german FROM "de_DE";
CREATE COLLATION french FROM "fr-x-icu";

23.2.2.4. 非確定性排序規(guī)則

排序規(guī)則要么是確定性的,要么是非確定性的。 確定性排序規(guī)則使用確定性比較方法,也就意味著字符串要認(rèn)為是相等的話,只有在它們是由相同的字節(jié)順序組成的時(shí)候。 非確定性比較可以確定字符串相等,即使它們由不同字節(jié)組成。 典型的情形包括大小寫敏感比較、重音敏感比較和不同Unicode規(guī)范形式的字符串的比較。 非敏感比較的實(shí)際是由排序規(guī)則提供程序來實(shí)現(xiàn)的;確定性標(biāo)志僅確定使用按字節(jié)比較時(shí)關(guān)系是否要斷開。 也可參見Unicode技術(shù)標(biāo)準(zhǔn)10獲得更多術(shù)語信息。

創(chuàng)建非確定性排序規(guī)則時(shí),指定屬性deterministic = falseCREATE COLLATION,例如:

CREATE COLLATION ndcoll (provider = icu, locale = 'und', deterministic = false);

該例子以非確定性方式使用標(biāo)準(zhǔn)的Unicode排序規(guī)則。特別地,這將允許不同規(guī)范形式的字符串能正確地比較。更有趣的例子是使用上述的ICU定制化功能。例如:

CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false);
CREATE COLLATION ignore_accents (provider = icu, locale = 'und-u-ks-level1-kc-true', deterministic = false);

所有標(biāo)準(zhǔn)和預(yù)定義的排序規(guī)則是確定性的,所有用戶自定義的排序規(guī)則缺省是確定性的。 盡管非確定性規(guī)則提供了更正確的行為,尤其是考慮到Unicode和其許多特殊情況的全部能力時(shí),也有一些缺點(diǎn)。 首先,使用它們會(huì)導(dǎo)致性能下降。特別請(qǐng)注意,B-tree不能將重復(fù)數(shù)據(jù)消除與使用非確定性排序的索引一起使用。 此外,用非確定性規(guī)則排序時(shí)某些操作就不可能做,例如模式匹配。因此,僅在特別需要它們的情況下才使用。

提示

要處理不同 Unicode 規(guī)范化格式中的文本,還有一個(gè)選項(xiàng),使用函數(shù)/表達(dá)式normalizeis normalized預(yù)處理或檢查字符串,而不是使用非確定性排序規(guī)則。每種方法都有不同的權(quán)衡。


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)