支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1

23.2. 排序規則支援 #

排序規則功能允許指定資料的排序順序和字元分類行為,可以針對每個資料欄位,甚至是每個操作。這減輕了資料庫建立後無法變更 LC_COLLATELC_CTYPE 設定的限制。

23.2.1. 概念 #

從概念上講,每個可排序資料類型的表達式都有一個排序規則。(內建的可排序資料類型為 textvarcharchar。使用者定義的基礎類型也可以標記為可排序,當然,在可排序資料類型之上的 網域也是可排序的。)如果表達式是資料欄位引用,則表達式的排序規則是該資料欄位定義的排序規則。如果表達式是一個常數,則排序規則是該常數資料類型的預設排序規則。更複雜的表達式的排序規則是從其輸入的排序規則中導出的,如下所述。

表達式的排序規則可以是預設排序規則,這表示為資料庫定義的語系設定。表達式的排序規則也可能是不確定的。在這種情況下,排序操作和其他需要知道排序規則的操作將會失敗。

當資料庫系統必須執行排序或字元分類時,它會使用輸入表達式的排序規則。例如,當使用 ORDER BY 子句以及函式或運算子呼叫(例如 <)時,就會發生這種情況。要應用於 ORDER BY 子句的排序規則只是排序鍵的排序規則。要應用於函式或運算子呼叫的排序規則是從引數中導出的,如下所述。除了比較運算子之外,排序規則還會被轉換大小寫字母的函式(例如 lowerupperinitcap)、樣式比對運算子以及 to_char 和相關函式所考慮。

對於函式或運算子呼叫,透過檢查引數排序規則而導出的排序規則在執行階段用於執行指定的作業。如果函式或運算子呼叫的結果是可排序的資料類型,則排序規則也會在剖析時間用作函式或運算子表達式的定義排序規則,以防有需要了解其排序規則的周圍表達式。

表達式的排序規則推導可以是隱含的或明確的。這種區別會影響當表達式中出現多個不同的排序規則時,如何組合排序規則。當使用 COLLATE 子句時,會發生明確的排序規則推導;所有其他排序規則推導都是隱含的。當需要組合多個排序規則時(例如在函式呼叫中),會使用以下規則

  1. 如果任何輸入表達式具有明確的排序規則推導,則輸入表達式中所有明確推導的排序規則必須相同,否則會引發錯誤。如果存在任何明確推導的排序規則,則它是排序規則組合的結果。

  2. 否則,所有輸入表達式都必須具有相同的隱含排序規則推導或預設排序規則。如果存在任何非預設排序規則,則它是排序規則組合的結果。否則,結果是預設排序規則。

  3. 如果輸入表達式之間存在衝突的非預設隱含排序規則,則認為該組合具有不確定的排序規則。除非被呼叫的特定函式需要知道它應該套用的排序規則,否則這不是錯誤狀況。如果確實需要,則會在執行階段引發錯誤。

例如,請考慮以下表格定義

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

然後在

SELECT a < 'foo' FROM test1;

中,< 比較會根據 de_DE 規則執行,因為表達式將隱含推導的排序規則與預設排序規則組合在一起。但是在

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

中,比較是使用 fr_FR 規則執行的,因為明確的排序規則推導會覆蓋隱含的排序規則。此外,給定

SELECT a < b FROM test1;

剖析器無法確定要套用哪個排序規則,因為 ab 資料欄位具有衝突的隱含排序規則。由於 < 運算子確實需要知道要使用哪個排序規則,因此這將導致錯誤。可以透過將明確的排序規則規範附加到任一輸入表達式來解決該錯誤,因此

SELECT a < b COLLATE "de_DE" FROM test1;

或等效地

SELECT a COLLATE "de_DE" < b FROM test1;

另一方面,在結構上類似的情況

SELECT a || b FROM test1;

不會導致錯誤,因為 || 運算子並不關心定序:其結果與定序無關。

如果函數或運算子的結果是可定序的資料類型,則分配給函數或運算子組合輸入表達式的定序也適用於函數或運算子的結果。所以在

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

排序將根據 de_DE 規則完成。但是這個查詢

SELECT * FROM test1 ORDER BY a || b;

會導致錯誤,因為即使 || 運算子不需要知道定序,ORDER BY 子句卻需要。與之前一樣,可以使用明確的定序規範來解決衝突

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

23.2.2. 管理定序 #

定序是一個 SQL 綱要物件,它將 SQL 名稱對應到作業系統中安裝的函式庫所提供的地區設定。定序定義具有一個提供者,用於指定哪個函式庫提供地區設定資料。一個標準的提供者名稱是 libc,它使用作業系統 C 函式庫提供的地區設定。這些是作業系統提供的大多數工具使用的地區設定。另一個提供者是 icu,它使用外部 ICU 函式庫。只有在建置 PostgreSQL 時配置了 ICU 支援,才能使用 ICU 地區設定。

libc 提供的定序物件會對應到 LC_COLLATELC_CTYPE 設定的組合,如 setlocale() 系統函式庫呼叫所接受的。(顧名思義,定序的主要目的是設定 LC_COLLATE,它控制排序順序。但在實務中,LC_CTYPE 設定與 LC_COLLATE 不同很少是必要的,因此將這些設定收集在一個概念下比為每個表達式建立另一個用於設定 LC_CTYPE 的基礎架構更方便。)此外,libc 定序與字元集編碼相關聯 (請參閱 第 23.3 節)。相同的定序名稱可能存在於不同的編碼中。

icu 提供的定序物件會對應到 ICU 函式庫提供的命名校對器。ICU 不支援單獨的 校對ctype 設定,因此它們始終相同。此外,ICU 定序與編碼無關,因此在資料庫中對於給定名稱的 ICU 定序始終只有一個。

23.2.2.1. 標準定序 #

在所有平台上,都支援以下定序

unicode

此 SQL 標準定序使用 Unicode 校對演算法和預設 Unicode 校對元素表進行排序。它適用於所有編碼。使用此定序需要 ICU 支援,如果使用不同版本的 ICU 建置 Postgres,則行為可能會變更。(此定序的行為與 ICU 根地區設定相同;請參閱 und-x-icu (對於 未定義)。)

ucs_basic

此 SQL 標準定序使用 Unicode 程式碼點值而非自然語言順序進行排序,並且只有 ASCII 字母 AZ 被視為字母。該行為在所有版本中都是高效且穩定的。僅適用於編碼 UTF8。(此定序的行為與 UTF8 編碼中的 libc 地區設定規範 C 相同。)

pg_c_utf8

此定序使用 Unicode 程式碼點值而非自然語言順序進行排序。對於函式 lowerinitcapupper,它使用 Unicode 簡單大小寫對應。對於模式比對 (包括正規表示式),它使用 Unicode 相容性屬性的 POSIX 相容變體。行為在 Postgres 主要版本中是高效且穩定的。此定序僅適用於編碼 UTF8

C (相當於 POSIX)

CPOSIX 定序基於 傳統 C 行為。它們按位元組值而非自然語言順序進行排序,並且只有 ASCII 字母 AZ 被視為字母。對於給定的資料庫編碼,該行為在所有版本中都是高效且穩定的,但不同資料庫編碼之間的行為可能會有所不同。

default

default 定序選擇在建立資料庫時指定的地區設定。

其他定序可能取決於作業系統支援而可用。這些額外定序的效率和穩定性取決於定序提供者、提供者版本和地區設定。

23.2.2.2. 預先定義的定序 #

如果作業系統提供在單一程式中使用多個地區設定的支援 (newlocale 和相關函式),或者如果配置了 ICU 的支援,則在初始化資料庫叢集時,initdb 會使用在當時在作業系統中找到的所有地區設定,來填入系統目錄 pg_collation

要檢查目前可用的地區設定,請使用查詢 SELECT * FROM pg_collation,或在 psql 中使用指令 \dOS+

23.2.2.2.1. libc 定序 #

例如,作業系統可能提供一個名為 de_DE.utf8 的地區設定。initdb 接著會為編碼 UTF8 建立一個名為 de_DE.utf8 的定序,其 LC_COLLATELC_CTYPE 都設定為 de_DE.utf8。它還會建立一個定序,其名稱去掉了 .utf8 標籤。因此,您也可以使用名稱 de_DE 下的定序,這樣寫起來不那麼麻煩,並且使名稱較不依賴編碼。請注意,儘管如此,定序名稱的初始設定是平台相關的。

libc 提供的預設定序集直接對應於作業系統中安裝的地區設定,可以使用指令 locale -a 列出這些地區設定。如果需要 libc 定序,其 LC_COLLATELC_CTYPE 的值不同,或者如果在初始化資料庫系統後在作業系統中安裝了新的地區設定,則可以使用 CREATE COLLATION 指令建立新的定序。也可以使用 pg_import_system_collations() 函式大量匯入新的作業系統地區設定。

在任何特定的資料庫中,只有使用該資料庫編碼的定序才受到關注。 pg_collation 中的其他項目會被忽略。因此,即使像 de_DE 這樣的精簡定序名稱,在給定的資料庫中也可以被認為是唯一的,即使它在全域中不是唯一的。建議使用精簡的定序名稱,因為如果您決定變更為其他資料庫編碼,您可以少變更一件事情。但請注意,無論資料庫編碼如何,都可以使用 defaultCPOSIX 定序。

PostgreSQL 認為即使具有相同屬性的不同定序物件也是不相容的。因此,舉例來說:

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

即使 CPOSIX 定序具有相同的行為,也會產生錯誤。因此,不建議混用已剝離和未剝離的定序名稱。

23.2.2.2.2. ICU 定序 #

使用 ICU 時,列舉所有可能的 locale 名稱是沒有意義的。ICU 對於 locale 使用特定的命名系統,但實際上的不同 locale 的命名方式遠多於實際存在的 locale 數量。initdb 使用 ICU API 來提取一組不同的 locale,以填充定序的初始集合。ICU 提供的定序會以 BCP 47 語言標籤格式在 SQL 環境中建立,並附加一個 私用 擴展 -x-icu,以將它們與 libc locale 區分開來。

以下是一些可能建立的定序範例:

de-x-icu #

德文定序,預設變體

de-AT-x-icu #

奧地利德文定序,預設變體

(例如,也有 de-DE-x-icude-CH-x-icu,但截至撰寫本文時,它們與 de-x-icu 等效。)

und-x-icu(適用於 未定義 #

ICU 定序。使用此選項可獲得合理的、與語言無關的排序順序。

ICU 不支援某些(較少使用的)編碼。當資料庫編碼是其中一種時,pg_collation 中的 ICU 定序項目會被忽略。嘗試使用其中一個會產生類似於 encoding "WIN874" doesn't have a collation "de-x-icu" 的錯誤訊息。

23.2.2.3. 建立新的定序物件 #

如果標準和預定義的定序不夠用,使用者可以使用 SQL 命令 CREATE COLLATION 建立自己的定序物件。

與所有預定義的物件一樣,標準和預定義的定序位於 schema pg_catalog 中。使用者定義的定序應在使用者 schema 中建立。這也確保它們可以被 pg_dump 儲存。

23.2.2.3.1. libc 定序 #

可以像這樣建立新的 libc 定序:

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

此命令中 locale 子句可接受的確切值取決於作業系統。在類 Unix 系統上,命令 locale -a 將顯示一個列表。

由於預定義的 libc 定序已經包含初始化資料庫實例時作業系統中定義的所有定序,因此通常不需要手動建立新的定序。原因可能是需要不同的命名系統(在這種情況下,另請參閱 第 23.2.2.3.3 節),或者作業系統已升級以提供新的 locale 定義(在這種情況下,另請參閱 pg_import_system_collations())。

23.2.2.3.2. ICU 定序 #

可以像這樣建立 ICU 定序:

CREATE COLLATION german (provider = icu, locale = 'de-DE');

ICU locale 指定為 BCP 47 語言標籤,但也接受大多數 libc 樣式的 locale 名稱。如果可能,libc 樣式的 locale 名稱會轉換為語言標籤。

新的 ICU 定序可以通過在語言標籤中包含定序屬性來廣泛地自定義定序行為。有關詳細資訊和範例,請參閱 第 23.2.3 節

23.2.2.3.3. 複製定序 #

命令 CREATE COLLATION 也可用於從現有定序建立新的定序,這對於能夠在應用程式中使用獨立於作業系統的定序名稱、建立相容性名稱或在更具可讀性的名稱下使用 ICU 提供的定序很有用。例如:

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

23.2.2.4. 非決定性定序 #

定序是決定性非決定性。決定性定序使用決定性比較,這意味著它僅在字串包含相同的位元組序列時才認為它們相等。非決定性比較可能會確定即使字串包含不同的位元組,它們也是相等的。典型的情況包括不區分大小寫的比較、不區分重音的比較以及不同 Unicode 正規化形式的字串比較。實際上由定序提供者來實現這種不敏感的比較;決定性標誌僅確定是否使用逐位比較來打破平局。有關術語的更多信息,另請參閱 Unicode 技術標準 10

要建立非決定性定序,請將屬性 deterministic = false 指定給 CREATE COLLATION,例如:

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

此範例將以非決定性的方式使用標準 Unicode 定序。特別是,這將允許正確比較不同正規化形式的字串。更具吸引力的範例利用了上面解釋的 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);

所有標準和預定義的定序都是決定性的,默認情況下所有使用者定義的定序都是決定性的。雖然非決定性定序給出了更正確的行為,尤其是在考慮到 Unicode 的全部功能及其許多特殊情況時,但它們也存在一些缺點。最重要的是,它們的使用會導致性能下降。特別要注意的是,B-tree 無法將重複資料刪除用於使用非決定性定序的索引。此外,某些操作無法使用非決定性定序,例如模式匹配操作。因此,它們僅應在特別需要它們的情況下使用。

提示

為了處理不同 Unicode 正規化形式的文本,也可以使用函數/表達式 normalizeis normalized 來預處理或檢查字串,而不是使用非決定性定序。每種方法都有不同的權衡。

23.2.3. ICU 自定義定序 #

ICU 允許通過定義新的定序,並將定序設定作為語言標籤的一部分,來廣泛控制定序行為。這些設定可以修改定序順序以滿足各種需求。例如:

-- ignore differences in accents and case
CREATE COLLATION ignore_accent_case (provider = icu, deterministic = false, locale = 'und-u-ks-level1');
SELECT 'Å' = 'A' COLLATE ignore_accent_case; -- true
SELECT 'z' = 'Z' COLLATE ignore_accent_case; -- true

-- upper case letters sort before lower case.
CREATE COLLATION upper_first (provider = icu, locale = 'und-u-kf-upper');
SELECT 'B' < 'b' COLLATE upper_first; -- true

-- treat digits numerically and ignore punctuation
CREATE COLLATION num_ignore_punct (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-kn');
SELECT 'id-45' < 'id-123' COLLATE num_ignore_punct; -- true
SELECT 'w;x*y-z' = 'wxyz' COLLATE num_ignore_punct; -- true

許多可用的選項在 第 23.2.3.2 節中描述,或者有關更多詳細資訊,請參閱 第 23.2.3.5 節

23.2.3.1. ICU 比較層級 #

在 ICU 中,兩個字串的比對(排序規則)是由一個多層級的過程決定,其中文字特徵被分組到「層級」中。每個層級的處理方式由排序規則設定控制。較高的層級對應於更精細的文字特徵。

表 23.1 顯示了在給定層級確定相等性時,哪些文字特徵的差異會被認為是重要的。Unicode 字元 U+2063 是一個不可見的分隔符,如表中所示,在小於 identic 的所有比較層級中都會被忽略。

表 23.1. ICU 排序規則層級

層級 描述 'f' = 'f' 'ab' = U&'a\2063b' 'x-y' = 'x_y' 'g' = 'G' 'n' = 'ñ' 'y' = 'z'
level1 基本字元 true true true true true false
level2 重音符號 true true true true false false
level3 大小寫/變體 true true true false false false
level4 標點符號[a] true true false false false false
identic 所有 true false false false false false

[a] 僅適用於 ka-shifted;請參閱表 23.2


在每個層級,即使關閉了完整的正規化,也會執行基本的正規化。例如,'á' 可能由程式碼點 U&'\0061\0301' 或單一程式碼點 U&'\00E1' 組成,並且即使在 identic 層級,這些序列也會被認為是相等的。要將程式碼點表示的任何差異視為不同,請使用將 deterministic 設定為 true 建立的排序規則。

23.2.3.1.1. 排序規則層級範例 #
CREATE COLLATION level3 (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-level3');
CREATE COLLATION level4 (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-level4');
CREATE COLLATION identic (provider = icu, deterministic = false, locale = 'und-u-ka-shifted-ks-identic');

-- invisible separator ignored at all levels except identic
SELECT 'ab' = U&'a\2063b' COLLATE level4; -- true
SELECT 'ab' = U&'a\2063b' COLLATE identic; -- false

-- punctuation ignored at level3 but not at level 4
SELECT 'x-y' = 'x_y' COLLATE level3; -- true
SELECT 'x-y' = 'x_y' COLLATE level4; -- false

23.2.3.2. ICU 地區設定的排序規則設定 #

表 23.2 顯示了可用的排序規則設定,這些設定可以用作語言標籤的一部分來客製化排序規則。

表 23.2. ICU 排序規則設定

預設值 描述
co emojiphonebkstandard... standard 排序規則類型。 有關其他選項和詳細資訊,請參閱第 23.2.3.5 節
ka noignoreshifted noignore 如果設定為 shifted,則會導致某些字元(例如標點符號或空格)在比較中被忽略。必須將鍵 ks 設定為 level3 或更低才能生效。設定鍵 kv 來控制要忽略的字元類別。
kb truefalse false 針對層級 2 差異進行反向比較。例如,地區設定 und-u-kb 會將 'àe' 排在 'aé' 之前。
kc truefalse false

將大小寫分隔為落在重音符號和其他層級 3 特徵之間的「層級 2.5」。

如果設定為 trueks 設定為 level1,則會忽略重音符號,但會考慮大小寫。

kf upperlowerfalse false 如果設定為 upper,則大寫字母排在小寫字母之前。如果設定為 lower,則小寫字母排在大寫字母之前。如果設定為 false,則排序取決於地區設定的規則。
kn truefalse false 如果設定為 true,則字串中的數字會被視為單一數值,而不是一連串的數字。例如,'id-45' 排在 'id-123' 之前。
kk truefalse false

啟用完整的正規化;可能會影響效能。即使設定為 false,也會執行基本的正規化。需要完整正規化的語言的地區設定通常預設啟用它。

在某些情況下,完整的正規化非常重要,例如將多個重音符號應用於單個字元時。例如,程式碼點序列 U&'\0065\0323\0302'U&'\0065\0302\0323' 表示以不同順序套用揚抑符和下點重音符的 e。啟用完整正規化後,這些程式碼點序列被視為相等;否則它們是不相等的。

kr spacepunctsymbolcurrencydigitscript-id  

設定為一個或多個有效值,或任何 BCP 47 script-id,例如 latn(「拉丁文」)或 grek(「希臘文」)。多個值以「-」分隔。

重新定義字元類別的排序;列表中較早的類別的字元在列表中較晚的類別的字元之前排序。例如,值 digit-currency-space(作為語言標籤的一部分,例如 und-u-kr-digit-currency-space)將標點符號排在數字和空格之前。

ks level1level2level3level4identic level3 確定相等性時的敏感度(或「強度」),其中 level1 對差異最不敏感,而 identic 對差異最敏感。有關詳細資訊,請參閱表 23.1
kv spacepunctsymbolcurrency punct 在層級 3 比較期間忽略的字元類別。設定為較晚的值包括較早的值;例如,symbol 也包括 punctspace 中要忽略的字元。必須將鍵 ka 設定為 shifted,並且必須將鍵 ks 設定為 level3 或更低才能生效。

預設值可能取決於地區設定。上表並非完整列表。 有關其他選項和詳細資訊,請參閱第 23.2.3.5 節

注意

對於許多校對設定,您必須建立校對規則,並將 deterministic 設定為 false,才能使設定達到預期效果(請參閱第 23.2.2.4 節)。此外,某些設定只有在將鍵 ka 設定為 shifted 時才會生效(請參閱表 23.2)。

23.2.3.3. 校對設定範例 #

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

使用電話簿校對類型的德文校對規則

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

具有 Emoji 校對類型的根校對規則,依照 Unicode Technical Standard #51

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

將希臘字母排序在拉丁字母之前。(預設為拉丁字母在希臘字母之前。)

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

將大寫字母排序在小寫字母之前。(預設為小寫字母優先。)

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

結合上述兩個選項。

23.2.3.4. ICU 客製化規則 #

如果上述校對設定提供的選項不足,可以使用客製化規則變更校對元素的順序,其語法詳述於 https://unicode-org.github.io/icu/userguide/collation/customization/

此小範例建立基於根語言環境並包含客製化規則的校對規則

CREATE COLLATION custom (provider = icu, locale = 'und', rules = '&V << w <<< W');

使用此規則,字母 W 排序在 V 之後,但被視為與重音符號相似的次要差異。 類似於這樣的規則包含在某些語言的語言環境定義中。(當然,如果語言環境定義已經包含所需的規則,則無需再次明確指定。)

以下是一個更複雜的範例。 以下語句建立一個名為 ebcdic 的校對規則,其中包含按 EBCDIC 編碼順序排序 US-ASCII 字元的規則。

CREATE COLLATION ebcdic (provider = icu, locale = 'und',
rules = $$
& ' ' < '.' < '<' < '(' < '+' < \|
< '&' < '!' < '$' < '*' < ')' < ';'
< '-' < '/' < ',' < '%' < '_' < '>' < '?'
< '`' < ':' < '#' < '@' < \' < '=' < '"'
<*a-r < '~' <*s-z < '^' < '[' < ']'
< '{' <*A-I < '}' <*J-R < '\' <*S-Z <*0-9
$$);

SELECT c
FROM (VALUES ('a'), ('b'), ('A'), ('B'), ('1'), ('2'), ('!'), ('^')) AS x(c)
ORDER BY c COLLATE ebcdic;
 c
---
 !
 a
 b
 ^
 A
 B
 1
 2

23.2.3.5. ICU 外部參考資料 #

本節(第 23.2.3 節)僅簡要概述 ICU 行為和語言標籤。 如需技術細節、其他選項和新行為,請參閱以下文件

提交更正

如果您在文件中發現任何不正確、與特定功能的體驗不符或需要進一步澄清的地方,請使用此表格報告文件問題。