排序规则支持

排序规则使您可以指定用于比较字符串的替代规则,从而根据特定语言或其他用户指定的规则对数据进行比较和排序。

排序规则支持概述

以下各节解释了什么是排序规则,以及在比较字符串时如何使用排序规则:

理解排序规则

Snowflake 中的文本字符串使用 UTF-8 字符集存储,默认情况下,系统根据表示字符串中字符的 Unicode 代码对字符串进行比较。

但是,根据字符串的 UTF-8 字符表示形式来比较字符串可能无法提供所需/预期的行为。例如:

  • 如果给定语言中的特殊字符未根据该语言的排序标准进行排序,则排序可能会返回意外结果。

  • 您可能希望字符串按其他规则排序,例如忽略字符的大小写情况。

排序规则使您可以根据以下内容显式指定用于比较字符串的规则:

  • 不同的区域设置(即对不同语言使用不同的字符集)。

  • 是否区分大小写(即在不显式调用 UPPERLOWER 函数来转换字符串的情况下,是使用区分大小写还是不区分大小写的字符串比较)。

  • 是否区分重音(例如 ZŹŻ 被视为同一字母还是不同字母)。

  • 是否区分标点符号(即比较时是仅使用字母还是包含所有字符)。例如,如果比较时不区分标点符号,则 A-B-CABC 被视为等效的字符串。

  • 其他选项,例如根据字符串中的第一个字母进行排序的偏好,以及修剪前导空格和/或末尾空格的偏好。

排序规则的用途

排序规则可用于多种操作,包括(但不限于):

用途

示例

链接

简单比较

... WHERE column1 = column2 ...

WHERE

联接

... ON table1.column1 = table2.column2 ...

JOIN

排序

... ORDER BY column1 ...

ORDER BY

Top-K 排序

... ORDER BY column1 LIMIT N ...

LIMIT / FETCH

聚合

... GROUP BY ...

GROUP BY

窗口子句

... PARTITION BY ... ORDER BY ...

窗口函数

标量函数

... LEAST(column1, column2, column3) ...

标量函数

聚合函数

... MIN(column1), MAX(column1) ...

聚合函数

数据聚类

... CLUSTER BY (column1) ...

群集密钥和聚类表

排序规则控制

排序规则控制是一种精细控制。您可以显式指定要用于以下内容的排序规则:

  • 账户(需使用账户级参数 DEFAULT_DDL_COLLATION)。

  • 添加到数据库的所有表中的所有列(需使用 ALTER DATABASE 命令)。

  • 添加到架构的所有表中的所有列(需使用 ALTER SCHEMA 命令)。

  • 添加到表中的所有列(需使用 ALTER TABLE 命令)。

  • 表中的单个列(需使用 CREATE TABLE 命令)。

  • SQL 语句中的特定比较(例如 WHERE col1 = col2)。如果对一个语句应用了多个排序规则,则 Snowflake 会根据优先级确定要使用的排序规则。有关优先级的更多详细信息,请参阅 多字符串操作中的排序规则优先级 (本主题内容)。

排序规则 SQL 构造

您可以为排序规则使用以下 SQL 构造:

COLLATE 子句(用于表列定义)

在表列定义中添加可选的 COLLATE 子句,表示在对列中数据进行比较和其他相关操作时使用指定的排序规则:

CREATE TABLE <table_name> ( <col_name> <col_type> COLLATE '<collation_specification>'
                            [ , <col_name> <col_type> COLLATE '<collation_specification>' ... ]
                            [ , ... ]
                          )
Copy

如果未对某列指定 COLLATE 子句,则 Snowflake 将使用默认设置,即根据字符串的 UTF-8 字符表示形式来比较字符串。

此外,Snowflake 还支持为排序规则规范指定一个空字符串(例如 COLLATE ''),这等效于不为列指定排序规则。

但请注意,由于优先级的原因,为列指定 COLLATE '' 与显式指定 COLLATE 'utf8' 的效果不同。有关更多详细信息,请参阅 多字符串操作中的排序规则优先级 (本主题内容)。

若要查看是否已为表中的列指定了排序规则,请使用 DESCRIBE TABLE (或使用 COLLATION 函数查看特定列的排序规则[如果有])。

COLLATE 函数

此函数对输入字符串表达式使用了指定的排序规则:

COLLATE( <expression> , '[<collation_specification>]' )
Copy

也可以使用中缀表示法调用此函数:

<expression> COLLATE '[<collation_specification>]'
Copy

此函数对于为特定操作(如排序)显式指定特定的排序规则特别有用,但它也可用于以下情形:

  • 允许在子查询的 SELECT 子句中使用排序规则,使外部查询中指定列上的所有操作都使用该排序规则。

  • 使用具有指定排序规则的 CTAS 创建表。

例如:

-- Evaluates using "English case-insensitive" collation:
SELECT * FROM t1 WHERE COLLATE(col1 , 'en-ci') = 'Tango';

-- Sorts the results using German (Deutsch) collation.
SELECT * FROM t1 ORDER BY COLLATE(col1 , 'de');

-- Creates a table with a column using French collation.
CREATE TABLE t2 AS SELECT COLLATE(col1, 'fr') AS col1 FROM t1;

-- Creates a table with a column using French collation.
CREATE TABLE t2 AS SELECT col1 COLLATE 'fr' AS col1 FROM t1;
Copy

COLLATION 函数

此函数返回表达式使用的排序规则规范,包括表列:

COLLATION( <expression> )
Copy

如果未为表达式指定排序规则,则函数返回 NULL

通常情况下,如果对列名称使用此函数,则应使用 DISTINCT 以避免表中每一行都有一行输出。例如:

SELECT DISTINCT COLLATION(column1) FROM table1;
Copy

备注

此函数仅返回排序规则规范,而不返回其优先级。有关优先级的更多详细信息,请参阅 多字符串操作中的排序规则优先级 (本主题内容)。

排序规则规范

使用 COLLATE 子句(用于表列)或 COLLATE 函数(用于表达式)时,必须包含排序规则规范,该规范可确定用于列/表达式的比较逻辑。

排序规则规范由一个或多个说明符组成的字符串组成,这些说明符用连字符 (-) 分隔,其形式为:

'<specifier>[-<specifier> ...]'

支持以下说明符(有关更多信息,请参阅本主题内容 支持的说明符):

  • 区域设置。

  • 是否区分大小写。

  • 是否区分重音。

  • 是否区分标点符号。

  • 首字母偏好。

  • 大小写转换。

  • 空格修剪。

说明符不区分大小写,可以按任何顺序排列,但区域设置除外,如果使用区域设置,则必须始终将其排在第一位。

以下部分提供了有关排序规则规范的更多详细信息:

规范示例

以下是排序规则规范字符串的一些示例:

  • 'de':德语 (Deutsch) 区域设置。

  • 'de-ci-pi':德语区域设置,比较时不区分大小写,也不区分标点符号。

  • 'fr_CA-ai':加拿大法语区域设置,比较时不区分重音。

  • 'en_US-trim':US 英语区域设置,比较之前会修剪前导空格和末尾空格。

还可以为排序规则规范指定一个空字符串(例如 COLLATE ''COLLATE(col1, '')),这表示不使用任何排序规则。

支持的说明符

区域设置:

指定要应用的特定于语言和特定于国家/地区的规则。

支持有效的区域设置字符串,由语言代码(必填)和国家/地区代码(可选)组成,格式为 language_country。以下是一些区域设置示例:

  • en – 英语。

  • en_US – 美式英语。

  • fr – 法语。

  • fr_CA – 加拿大法语。

此外,utf8 伪区域设置指定使用 Unicode 排序,这是默认设置。有关更多详细信息,请参阅 :ref:`label-collation_utf8_vs_locale`(本主题内容)。

区域设置说明符是可选的,但如果使用,则必须是字符串中的 第一个 说明符。

是否区分大小写:

确定在比较值时是否应考虑大小写。可能的值如下:

  • cs – 区分大小写(默认)。

  • ci – 不区分大小写。

例如:

排序规则 规范

结果

'en-ci'

Abc = abc

True

'en-cs' / en

Abc = abc

False

是否区分重音:

确定重音字符应该被视为与其基本字符相等还是不同。可能的值如下:

  • as – 区分重音(默认)。

  • ai – 不区分重音。

例如:

排序规则 规范

结果

备注

'fr-ai'

E = É

True

'fr-as' / 'fr'

E = É

False

'en-ai'

a = ą

True

在英语中,这些字母被视为只有重音差异,因此指定不区分重音会导致比较的值相等。

'pl-ai'

a = ą

False

在波兰语中,这些字母被视为独立的基本字母,因此无论是否指定了不区分重音,它们在比较时总是不相等的。

'pl-as' / 'pl'

a = ą

False

请注意,有关是否区分重音的规则和排序规则因语言而异。例如,在某些语言中,排序规则始终是区分重音的,即使指定了不区分重音的排序规则,也无法将其关闭。

是否区分标点符号:

确定非字母字符是否重要。可能的值如下:

  • ps – 区分标点符号。

  • pi – 不区分标点符号。

请注意,默认值是特定于区域设置的(即,如果未指定是否区分标点符号,则使用特定于区域设置的规则)。在大多数情况下,规则等效于 ps

例如:

排序规则 规范

结果

备注

'en-pi'

A-B-C = ABC

True

'en-ps'

A-B-C = ABC

False

首字母偏好:

确定排序时,大写字母还是小写字母应排序在先。可能的值如下:

  • fl – 小写字母排序在先。

  • fu – 大写字母排序在先。

请注意,默认值是特定于区域设置的(即,如果未指定任何值,则使用特定于区域设置的排序)。在大多数情况下,排序等效于 fl

此外,此说明符对相等性比较没有影响。

大小写转换:

导致字符串在比较之前转换为小写或大写。在某些情况下,这种转换比完全特定于区域设置的排序规则更快。可能的值如下:

  • upper – 在比较之前将字符串转换为大写。

  • lower – 在比较之前将字符串转换为小写。

请注意,此说明符没有默认值(即,如果未指定任何值,则这两种转换都不会发生)。

空格修剪:

在比较之前从字符串中移除前导/末尾空格。此功能对执行与 SQL CHAR 数据类型在语义上等效的比较(在极少数极端情况下除外)非常有用。

可能的值如下:

  • trim – 在比较之前移除前导空格和末尾空格。

  • ltrim – 在比较之前仅移除前导空格。

  • rtrim – 在比较之前仅移除末尾空格。

请注意,此说明符没有默认值(即,如果未指定任何值,则不执行修剪)。

例如:

排序规则 规范

结果

备注

'en-trim'

__ABC_ = ABC

True

出于示例目的,下划线字符表示空格。

'en-ltrim'

__ABC_ = ABC

False

'en-rtrim'

__ABC_ = ABC

False

'en'

__ABC_ = ABC

False

排序规则实施细节

以下各节提供了有关排序规则支持的更多详细信息:

不区分大小写的比较

以下各节介绍不区分大小写的比较:

比较大写字符串和原始字符串时的差异

在某些语言中,两个小写字符对应的大写字符相同。例如,小写 I 时,某些语言同时支持带点和不带点形式(例如,iı)。强制将字符串转换为大写会影响比较。

下面的示例说明了这一区别:

创建表:

create or replace table test_table (col1 varchar, col2 varchar);
insert into test_table values ('ı', 'i');
Copy

查询数据:

select col1 = col2,
       COLLATE(col1, 'lower') = COLLATE(col2, 'lower'),
       COLLATE(col1, 'upper') = COLLATE(col2, 'upper')
    from test_table;
+-------------+-------------------------------------------------+-------------------------------------------------+
| COL1 = COL2 | COLLATE(COL1, 'LOWER') = COLLATE(COL2, 'LOWER') | COLLATE(COL1, 'UPPER') = COLLATE(COL2, 'UPPER') |
|-------------+-------------------------------------------------+-------------------------------------------------|
| False       | False                                           | True                                            |
+-------------+-------------------------------------------------+-------------------------------------------------+
Copy

字符权重

Snowflake 支持以下 排序规则规范

  • ICU (link removed) (Unicode 国际化组件)。

  • 特定于 Snowflake 的排序规则规范(例如 upperlower)。

对于 ICU 定义的不区分大小写的比较操作,Snowflake 遵循 Unicode 排序规则算法 (UCA) (http://www.unicode.org/reports/tr10),仅考虑 Unicode 字符的一级和二级权重,而不考虑三级权重。仅三级权重不同的字符被视为相同字符。例如,使用 en-ci 排序规则规范时,空格和不间断空格被视为相同。

使用 UTF-8 或区域排序规则时的排序差异

字符串总是以 UTF-8 的形式存储在 Snowflake 内部,并且可以表示 UTF-8 支持的任何语言中的任何字符;因此,默认的排序规则为 UTF-8(即 'utf8')。

UTF-8 排序规则基于字符的数字表示形式,而不是字符的字母顺序。

这类似于按每个 ASCII 字符的序数值进行排序,这一点很重要,因为大写字母的序数值小于小写字母:

A = 65
B = 66
...
a = 97
b = 98
...

因此:

  • 如果按 UTF-8 顺序排序,则所有大写字母都会先于所有小写字母返回:

    A , B , ... , Y , Z , ... , a , b , ... , y , z

  • 相比之下,'en' 排序规则规范按字母顺序排序(而不是使用 UTF-8 内部表示形式),结果是 Aa 先于 Bb 返回:

    a , A , b , B , ...

此外,是否区分大小写的说明符 csci 之间的差异会影响排序:

  • cs (区分大小写)总是先返回某字母的小写形式,再返回其大写形式。例如,使用 'en-cs'

    a , A , b , B , ...

    请注意,默认情况下区分大小写,因此 'en-cs''en' 是等效的。

  • ci (不区分大小写)随机返回字母的大小写形式,但会先返回前一字母的大小写形式,再返回后一字母的大小写形式。例如,使用 'en-ci'

    A , a , b , B , ...

根据排序规则设置的不同,某些非字母字符的排序可能也会不同。下面的代码示例演示对于不同的排序规则设置,加号字符 (+) 和减号字符 (-) 的排序方式不同:

创建表:

create or replace table demo (
    no_explicit_collation VARCHAR,
    en_ci VARCHAR COLLATE 'en-ci',
    en VARCHAR COLLATE 'en',
    utf_8 VARCHAR collate 'utf8');
insert into demo (no_explicit_collation) values
    ('-'),
    ('+');
update demo SET
    en_ci = no_explicit_collation,
    en = no_explicit_collation,
    utf_8 = no_explicit_collation;
Copy

查询数据:

select max(no_explicit_collation), max(en_ci), max(en), max(utf_8)
    from demo;
+----------------------------+------------+---------+------------+
| MAX(NO_EXPLICIT_COLLATION) | MAX(EN_CI) | MAX(EN) | MAX(UTF_8) |
|----------------------------+------------+---------+------------|
| -                          | +          | +       | -          |
+----------------------------+------------+---------+------------+
Copy

多字符串操作中的排序规则优先级

对两个(或多个)字符串执行操作时,可能会为不同的字符串指定不同的排序规则。确定要应用的排序规则取决于为每个输入指定排序规则的方式,以及每个说明符的优先级。

有 3 个优先级(从最高到最低):

函数:

在 SQL 语句中使用 COLLATE 函数 函数来指定排序规则。

:

在列定义中指定排序规则。

:

没有为给定的表达式/列指定排序规则,或者使用了具有空规范的排序规则(例如 COLLATE(col1, '')col1 STRING COLLATE '')。

确定要使用的排序规则时,系统将使用优先级 最高 的排序规则规范。如果指定了多个排序规则并且它们具有相同的优先级,系统将比较它们的值,如果这些值不相等,则返回错误。

例如,请考虑具有下述列级排序规则规范的表:

CREATE OR REPLACE TABLE collation_precedence_example(
  col1    VARCHAR,               -- equivalent to COLLATE ''
  col2_fr VARCHAR COLLATE 'fr',  -- French locale
  col3_de VARCHAR COLLATE 'de'   -- German locale
);
Copy

如果在比较两个字符串的语句中使用该表,则系统将按如下方式应用排序规则:

-- Uses the 'fr' collation because the precedence for col2_fr is higher than
-- the precedence for col1.
... WHERE col1 = col2_fr ...

-- Uses the 'en' collation, because it is explicitly specified in the statement,
-- which takes precedence over the collation for col2_fr.
... WHERE col1 COLLATE 'en' = col2_fr ...

-- Returns an error because the expressions have different collations at the same
-- precedence level.
... WHERE col2_fr = col3_de ...

-- Uses the 'de' collation because collation for col2_fr has been removed.
... WHERE col2_fr COLLATE '' = col3_de ...

-- Returns an error because the expressions have different collations at the same
-- precedence level.
... WHERE col2_fr COLLATE 'en' = col3_de COLLATE 'de' ...
Copy

备注

即使 Snowflake 的默认排序规则是 'utf8',指定空字符串(或不指定排序规则)也不同于显式指定 'utf8',因为显式排序规则的优先级高于无排序规则。以下代码示例中的最后两条语句显示了这种差异:

CREATE OR REPLACE TABLE collation_precedence_example2(
  s1 STRING COLLATE '',
  s2 STRING COLLATE 'utf8',
  s3 STRING COLLATE 'fr'
);

-- Uses 'utf8' because s1 has no collation and 'utf8' is the default.
SELECT * FROM collation_precedence_example2 WHERE s1 = 'a';

-- Uses 'utf8' because s1 has no collation and s2 has explicit 'utf8' collation.
SELECT * FROM collation_precedence_example2 WHERE s1 = s2;
Copy

此示例执行时没有错误,因为 s1 没有排序规则,而 s3 有显式 fr 排序规则,因此显式排序规则优先:

SELECT * FROM collation_precedence_example2 WHERE s1 = s3;
+----+----+----+
| S1 | S2 | S3 |
|----+----+----|
+----+----+----+
Copy

此示例会导致错误,因为 s2 和 s3 在同一优先级指定了不同的排序规则:

SELECT * FROM collation_precedence_example2 WHERE s2 = s3;
Copy

输出:

002322 (42846): SQL compilation error: Incompatible collations: 'fr' and 'utf8'
Copy

对内置函数中排序规则的有限支持

只有一部分字符串函数支持排序规则。对于可以合理预期实施排序规则但尚不支持排序规则的函数,其与排序规则一起使用时会返回错误。这些错误消息不仅在调用 COLLATE 函数时显示,而且在对某列(创建该列的 CREATE TABLEALTER TABLE 语句中将该列定义为已排序)调用字符串函数时也会显示。

目前,排序规则仅影响简单的比较操作。

例如,即使指定了不区分大小写的排序规则,POSITION('abc' in COLLATE('ABC', 'en-ci')) 也无法在 ABC 中找出 abc

支持排序规则的函数

以下函数支持排序规则:

其中一些函数在与排序规则一起使用时存在限制。有关信息,请参阅每个具体函数的文档。

此列表可能会随着时间的推移而扩充。

小心

一些 SQL 运算符和谓词,例如 || (串联)和 LIKE,可作为函数实现(并且可作为函数使用,例如 LIKE()CONCAT())。如果谓词或运算符作为函数实现,并且该函数不支持排序规则,则该谓词或运算符也不支持排序规则。

另请参阅 排序规则限制

使用排序规则对性能的影响

使用排序规则可能会影响多种数据库操作的性能:

  • 涉及比较的操作可能速度会较慢。

    这可能会影响简单 WHERE 子句,以及联接、排序、GROUP BY 操作等。

  • 当与 WHERE 谓词中的某些函数一起使用时,微分区修剪的效率可能会降低。

  • WHERE 谓词中使用的排序规则与为列指定的排序规则不同时,可能会导致修剪效率降低或完全消除修剪。

使用排序规则的其他注意事项

  • 请记住,以下排序规则函数尽管名称相似,但返回的结果不同:

    • COLLATE 显式指定要使用的排序规则。

    • COLLATION 显示在未显式指定任何排序规则的情况下所使用的排序规则。

  • 具有排序规则规范的列可以使用并非来自该排序规则区域设置中的字符,从而可能会影响排序。

    例如,如果使用 COLLATE 'en' 子句创建列,则该列中的数据可以包含非英语字符 É。在这种情况下,字符 É 的排序接近 E

  • 您可以指定不一定有意义的排序规则操作。

    例如,您可以指定使用德语排序规则将波兰语数据与法语数据进行比较:

    SELECT ... WHERE COLLATE(French_column, 'de') = Polish_column;
    
    Copy

    但是,Snowflake 建议以这种方式使用该功能,因为这样可能会返回意外的结果。

  • 定义表列后,无法更改该列的排序规则。换言之,在使用 CREATE TABLE 语句创建具有特定排序规则的列后,不能再使用 ALTER TABLE 更改排序规则。

    但是,可以在引用该列的 DML 语句(如 SELECT 语句)中指定不同的排序规则。

ciupper / lower 的区别

在字符串比较和排序过程中,upperlower 排序规则规范可以提供比 ci 排序规则规范更好的性能。但是,upperlower 的效果与 ci 稍有不同,将在以下部分中解释:

宽度、空格和脚本比较的差异

在字符串比较期间,ci 排序规则规范识别到字符的不同视觉表示仍然可能引用同一字符,并相应地处理它们。为了进行更多项性能比较,upperlower 排序规则规范不将这些字符的不同视觉表示识别为同一字符。

具体而言,ci 排序规则规范忽略了以下类别中的一些差异,而 upperlower 排序规则规范没有忽略它们:

以下各节举例说明了这些差异。

备注

全宽和半宽字符的比较行为可能取决于区域设置。

不同宽度字符比较示例

创建名为 different_widths 的表,并插入包含不同宽度字符的行:

CREATE OR REPLACE TABLE different_widths(codepoint STRING, description STRING);

INSERT INTO different_widths VALUES
  ('a', 'ASCII a'),
  ('A', 'ASCII A'),
  ('a', 'Full-width a'),
  ('A', 'Full-width A');

SELECT codepoint VISUAL_CHAR,
       'U+'  || TO_CHAR(UNICODE(codepoint), '0XXX') codepoint_representation,
       description
  FROM different_widths;
Copy
+-------------+--------------------------+--------------+
| VISUAL_CHAR | CODEPOINT_REPRESENTATION | DESCRIPTION  |
|-------------+--------------------------+--------------|
| a           | U+0061                   | ASCII a      |
| A           | U+0041                   | ASCII A      |
| a          | U+FF41                   | Full-width a |
| A          | U+FF21                   | Full-width A |
+-------------+--------------------------+--------------+

下面的查询显示,ci 排序规则规范在比较字符时找到一个不同的值。upperlower 排序规则规范在比较字符时找到两个不同的值。

SELECT COUNT(*) NumRows,
       COUNT(DISTINCT UNICODE(codepoint)) DistinctCodepoints,
       COUNT(DISTINCT codepoint COLLATE 'en-ci') DistinctCodepoints_EnCi,
       COUNT(DISTINCT codepoint COLLATE 'upper') DistinctCodepoints_Upper,
       COUNT(DISTINCT codepoint COLLATE 'lower') DistinctCodepoints_Lower
  FROM different_widths;
Copy
+---------+--------------------+-------------------------+--------------------------+--------------------------+
| NUMROWS | DISTINCTCODEPOINTS | DISTINCTCODEPOINTS_ENCI | DISTINCTCODEPOINTS_UPPER | DISTINCTCODEPOINTS_LOWER |
|---------+--------------------+-------------------------+--------------------------+--------------------------|
|       4 |                  4 |                       1 |                        2 |                        2 |
+---------+--------------------+-------------------------+--------------------------+--------------------------+

ci 排序规则规范会忽略宽度和大小写上的差异,这意味着它找不到字符之间的差异。upperlower 排序规则规范仅忽略大小写差异,因此半宽字符被视为与全宽字符不同的字符。

半宽小写 a 被视为与半宽大写 A 相同,全宽小写 a 被视为与全宽大写 A 相同。因此,upperlower 排序规则规范找到两个不同的值。

不同空格类型的比较示例

创建名为 different_whitespaces 的表并插入具有不同空格类型的行:

CREATE OR REPLACE TABLE different_whitespaces(codepoint STRING, description STRING);

INSERT INTO different_whitespaces VALUES
  (' ', 'ASCII space'),
  ('\u00A0', 'Non-breaking space'),
  (' ', 'Ogham space mark'),
  (' ', 'en space'),
  (' ', 'em space');

SELECT codepoint visual_char,
       'U+'  || TO_CHAR(unicode(codepoint), '0XXX')
       codepoint_representation, description
  FROM different_whitespaces;
Copy
+-------------+--------------------------+--------------------+
| VISUAL_CHAR | CODEPOINT_REPRESENTATION | DESCRIPTION        |
|-------------+--------------------------+--------------------|
|             | U+0020                   | ASCII space        |
|             | U+00A0                   | Non-breaking space |
|             | U+1680                   | Ogham space mark   |
|             | U+2002                   | en space           |
|             | U+2003                   | em space           |
+-------------+--------------------------+--------------------+

下面的查询显示,ci 排序规则规范在比较空格时找到一个不同的值,这意味着它们之间没有差异。upperlower 排序规则规范在比较空格时找到五个不同的值,这意味着它们都是不同的。

SELECT COUNT(*) NumRows,
       COUNT(DISTINCT UNICODE(codepoint)) NumDistinctCodepoints,
       COUNT(DISTINCT codepoint COLLATE 'en-ci') DistinctCodepoints_EnCi,
       COUNT(DISTINCT codepoint COLLATE 'upper') DistinctCodepoints_Upper,
       COUNT(DISTINCT codepoint COLLATE 'lower') DistinctCodepoints_Lower
  FROM different_whitespaces;
Copy
+---------+-----------------------+-------------------------+--------------------------+--------------------------+
| NUMROWS | NUMDISTINCTCODEPOINTS | DISTINCTCODEPOINTS_ENCI | DISTINCTCODEPOINTS_UPPER | DISTINCTCODEPOINTS_LOWER |
|---------+-----------------------+-------------------------+--------------------------+--------------------------|
|       5 |                     5 |                       1 |                        5 |                        5 |
+---------+-----------------------+-------------------------+--------------------------+--------------------------+

不同脚本的字符比较示例

创建名为 different_scripts 的表,并插入包含使用不同脚本的字符的行:

CREATE OR REPLACE TABLE different_scripts(codepoint STRING, description STRING);

INSERT INTO different_scripts VALUES
  ('1', 'ASCII digit 1'),
  ('¹', 'Superscript 1'),
  ('₁', 'Subscript 1'),
  ('①', 'Circled digit 1'),
  ('੧', 'Gurmukhi digit 1'),
  ('௧', 'Tamil digit 1');

SELECT codepoint VISUAL_CHAR,
       'U+'  || TO_CHAR(UNICODE(codepoint), '0XXX') codepoint_representation,
       description
  FROM different_scripts;
Copy
+-------------+--------------------------+------------------+
| VISUAL_CHAR | CODEPOINT_REPRESENTATION | DESCRIPTION      |
|-------------+--------------------------+------------------|
| 1           | U+0031                   | ASCII digit 1    |
| ¹           | U+00B9                   | Superscript 1    |
| ₁           | U+2081                   | Subscript 1      |
| ①           | U+2460                   | Circled digit 1  |
| ੧           | U+0A67                   | Gurmukhi digit 1 |
| ௧           | U+0BE7                   | Tamil digit 1    |
+-------------+--------------------------+------------------+

下面的查询显示,ci 排序规则规范在比较字符时找到一个不同的值,这意味着它们之间没有差异。upperlower 排序规则规范在比较字符时找到六个不同的值,这意味着它们都是不同的。

SELECT COUNT(*) NumRows,
       COUNT(DISTINCT UNICODE(codepoint)) DistinctCodepoints,
       COUNT(DISTINCT codepoint COLLATE 'en-ci') DistinctCodepoints_EnCi,
       COUNT(DISTINCT codepoint COLLATE 'upper') DistinctCodepoints_Upper,
       COUNT(DISTINCT codepoint COLLATE 'lower') DistinctCodepoints_Lower
  FROM different_scripts;
Copy
+---------+--------------------+-------------------------+--------------------------+--------------------------+
| NUMROWS | DISTINCTCODEPOINTS | DISTINCTCODEPOINTS_ENCI | DISTINCTCODEPOINTS_UPPER | DISTINCTCODEPOINTS_LOWER |
|---------+--------------------+-------------------------+--------------------------+--------------------------|
|       6 |                  6 |                       1 |                        6 |                        6 |
+---------+--------------------+-------------------------+--------------------------+--------------------------+

处理可忽略代码点的差异

Unicode 排序规则算法规定排序规则元素(代码点)可以是 可忽略的 (https://www.unicode.org/reports/tr10/tr10-36.html#Ignorables_Defn),这意味着在字符串比较和排序时不考虑代码点。

  • 使用 ci 排序规则规范,这些代码点将被忽略。这会使搜索或替换可忽略的代码点变得困难。

  • 使用 upperlower 排序规则规范,这些代码点就不会被忽略。

例如,代码点 U+0001 可以忽略。如果您将此代码点与具有 en-ci 排序规则规范的空字符串进行比较,则结果为 TRUE,因为 U+0001 会被忽略:

SELECT '\u0001' = '' COLLATE 'en-ci';
Copy
+-------------------------------+
| '\U0001' = '' COLLATE 'EN-CI' |
|-------------------------------|
| True                          |
+-------------------------------+

另一方面,如果使用 upperlower 排序规则规范,则结果为 FALSE,因为 U+0001 不会被忽略:

SELECT '\u0001' = '' COLLATE 'upper';
Copy
+-------------------------------+
| '\U0001' = '' COLLATE 'UPPER' |
|-------------------------------|
| False                         |
+-------------------------------+

同样,假设您调用 REPLACE 函数从字符串中移除此代码点。如果使用 en-ci 排序规则规范,则函数不会移除代码点,因为 U+0001 会被忽略。

如下例所示,REPLACE 函数返回的字符串与传入函数的字符串具有相同的长度,因为函数不会移除 U+0001 字符。

SELECT
  LEN('abc\u0001') AS original_length,
  LEN(REPLACE('abc\u0001' COLLATE 'en-ci', '\u0001')) AS length_after_replacement;
Copy
+-----------------+--------------------------+
| ORIGINAL_LENGTH | LENGTH_AFTER_REPLACEMENT |
|-----------------+--------------------------|
|               4 |                        4 |
+-----------------+--------------------------+

另一方面,如果使用 upperlower 排序规则规范,该函数将从字符串中移除代码点,返回较短的字符串。

SELECT
  LEN('abc\u0001') AS original_length,
  LEN(REPLACE('abc\u0001' COLLATE 'upper', '\u0001')) AS length_after_replacement;
Copy
+-----------------+--------------------------+
| ORIGINAL_LENGTH | LENGTH_AFTER_REPLACEMENT |
|-----------------+--------------------------|
|               4 |                        3 |
+-----------------+--------------------------+

字符由不同代码点表示时的差异

在 Unicode 中,不同的码点序列可以表示相同的字符 (link removed)。例如,带有 Dialytika 和 Tonos 的希腊小写字母 Iota 可以用带有代码点 U+0390 的 预组合字符 (link removed) 表示,也可以用分解字符的代码点 U+03b9 U+0308 U+0301 序列表示。

如果使用 ci 排序规则规范,字符的不同代码点序列将被视为同一字符。例如,代码点 U+0390 和代码点序列 U+03b9 U+0308 U+0301 被视为同一字符:

SELECT '\u03b9\u0308\u0301' = '\u0390' COLLATE 'en-ci';
Copy
+-------------------------------------------------+
| '\U03B9\U0308\U0301' = '\U0390' COLLATE 'EN-CI' |
|-------------------------------------------------|
| True                                            |
+-------------------------------------------------+

为了提高 upperlower 排序规则规范的性能,序列并没有以同样的方式处理。只有当两个代码点序列转换为大写或小写后得到相同的二进制表示形式时,它们才被认为是相同的。

例如,将 upper 规范与代码点 U+0390 和代码点序列 U+03b9 U+0308 U+0301 一起使用会导致字符被视为相同:

SELECT '\u03b9\u0308\u0301' = '\u0390' COLLATE 'upper';
Copy
+-------------------------------------------------+
| '\U03B9\U0308\U0301' = '\U0390' COLLATE 'UPPER' |
|-------------------------------------------------|
| True                                            |
+-------------------------------------------------+

使用 lower 规范会导致字符不相同:

SELECT '\u03b9\u0308\u0301' = '\u0390' COLLATE 'lower';
Copy
+-------------------------------------------------+
| '\U03B9\U0308\U0301' = '\U0390' COLLATE 'LOWER' |
|-------------------------------------------------|
| False                                           |
+-------------------------------------------------+

在使用 upper (而不是 lower)时,这些差异不太可能发生,因为只有一个复合大写代码点 (U+0130),而有超过 100 个复合小写代码点。

与代表单个字符的代码点序列的区别

在代码点序列表示单个字符的情况下,ci 排序规则规范会识别该序列表示单个字符,并且与序列中的单个代码点不匹配。

例如,代码点序列 U+03b9 U+0308 U+0301 表示单个字符(带有 Dialytika 和 Tonos 的希腊文小写字母 Iota)。U+0308U+0301 表示应用于 U+03b9 的重音。

对于 ci 排序规则规范,如果使用 CONTAINS 函数来确定序列 U+03b9 U+0308 是否包含 U+03b9U+0308,该函数将返回 FALSE,因为序列 U+03b9 U+0308 被视为单个字符:

SELECT CONTAINS('\u03b9\u0308', '\u03b9' COLLATE 'en-ci');
Copy
+----------------------------------------------------+
| CONTAINS('\U03B9\U0308', '\U03B9' COLLATE 'EN-CI') |
|----------------------------------------------------|
| False                                              |
+----------------------------------------------------+
SELECT CONTAINS('\u03b9\u0308', '\u0308' COLLATE 'en-ci');
Copy
+----------------------------------------------------+
| CONTAINS('\U03B9\U0308', '\U0308' COLLATE 'EN-CI') |
|----------------------------------------------------|
| False                                              |
+----------------------------------------------------+

为了提高性能,upperlower 规范不将这些序列视为单个字符。在上面的示例中,CONTAINS 函数返回 TRUE,因为这些规范将代码点序列视为单独的字符:

SELECT CONTAINS('\u03b9\u0308', '\u03b9' COLLATE 'upper');
Copy
+----------------------------------------------------+
| CONTAINS('\U03B9\U0308', '\U03B9' COLLATE 'UPPER') |
|----------------------------------------------------|
| True                                               |
+----------------------------------------------------+
SELECT CONTAINS('\u03b9\u0308', '\u0308' COLLATE 'upper');
Copy
+----------------------------------------------------+
| CONTAINS('\U03B9\U0308', '\U0308' COLLATE 'UPPER') |
|----------------------------------------------------|
| True                                               |
+----------------------------------------------------+

当更改大小写导致多个代码点变化时的差异

对于某些复合字符,字符的大写或小写版本由代码点序列表示。例如,德语字符 ß 的大写字符是两个 S 字符 (SS) 的序列。

尽管 ß 和 SS 是相同的,但当您使用 upper 排序规则规范时,ß 和 SS 的搜索会返回不同的结果。大小写转换产生的序列要么完全相符,要么根本不符。

SELECT CONTAINS('ß' , 's' COLLATE 'upper');
Copy
+--------------------------------------+
| CONTAINS('SS' , 'S' COLLATE 'UPPER') |
|--------------------------------------|
| False                                |
+--------------------------------------+
SELECT CONTAINS('ss', 's' COLLATE 'upper');
Copy
+-------------------------------------+
| CONTAINS('SS', 'S' COLLATE 'UPPER') |
|-------------------------------------|
| True                                |
+-------------------------------------+

排序顺序的差异

upperlower 排序规则规范的排序与 ci 规范的排序不同:

  • 使用 ci 规范,字符串按排序规则键排序。一般来说,排序规则键可以考虑大小写敏感性、重音敏感性、区域设置等。

  • 使用 upper``lower``规范,字符串按代码点排序以提高性能。

例如,ASCII 范围内的一些字符(如 +-)排序不同:

SELECT '+' < '-' COLLATE 'en-ci';
Copy
+---------------------------+
| '+' < '-' COLLATE 'EN-CI '|
|---------------------------|
| False                     |
+---------------------------+
SELECT '+' < '-' COLLATE 'upper';
Copy
+---------------------------+
| '+' < '-' COLLATE 'UPPER' |
|---------------------------|
| True                      |
+---------------------------+

在另一个示例中,带有被忽略代码点的字符串会以不同的顺序排序:

SELECT 'a\u0001b' < 'ab' COLLATE 'en-ci';
Copy
+-----------------------------------+
| 'A\U0001B' < 'AB' COLLATE 'EN-CI' |
|-----------------------------------|
| False                             |
+-----------------------------------+
SELECT 'a\u0001b' < 'ab' COLLATE 'upper';
Copy
+-----------------------------------+
| 'A\U0001B' < 'AB' COLLATE 'UPPER' |
|-----------------------------------|
| True                              |
+-----------------------------------+

此外,表情符号排序方式也不同:

SELECT 'abc' < '❄' COLLATE 'en-ci';
Copy
+-----------------------------+
| 'ABC' < '❄' COLLATE 'EN-CI' |
|-----------------------------|
| False                       |
+-----------------------------+
SELECT 'abc' < '❄' COLLATE 'upper';
Copy
+-----------------------------+
| 'ABC' < '❄' COLLATE 'UPPER' |
|-----------------------------|
| True                        |
+-----------------------------+

排序规则限制

以下限制适用于排序规则:

排序规则仅支持不超过 8MB 的字符串

尽管 Snowflake VARCHAR 数据类型最大可支持 16MB 的字符串,但仅当 生成的 字符串不大于 8MB 时,Snowflake 才支持排序规则。(某些排序规则操作可能会使字符串变长。)

UDFs 不支持排序规则

Snowflake 不支持使用了 UDFs (用户定义的函数)的排序规则:

  • 不能从 UDF 返回已排序的字符串值;服务器会反馈实际返回类型与声明的返回类型不兼容。

  • 如果将已排序的字符串值传递给 UDF,则系统不会传递排序规则信息;UDF 会将字符串视为未排序的字符串。

VARIANT、ARRAY 或 OBJECT 中的字符串不支持排序规则

存储在 VARIANT、OBJECT 或 ARRAY 中的字符串不包含排序规则规范。因此:

  • 这些值的比较始终使用“utf8”排序规则。

  • 当使用具有排序规则规范的 VARCHAR 值来构造 ARRAY、OBJECT 或 VARIANT 值时,系统不会保留排序规则规范。

  • 对于存储在 ARRAY、OBJECT 或 VARIANT 中的值,用户仍然可以通过提取该值、将其类型转换为 VARCHAR 并添加排序规则规范来进行比较。例如:

    COLLATE(VARIANT_COL:fld1::VARCHAR, 'en-ci') = VARIANT_COL:fld2::VARCHAR
    
    Copy

排序规则示例

以下语句创建了一个表,该表对每列使用不同的排序规则:

CREATE TABLE collation_demo (
  uncollated_phrase VARCHAR, 
  utf8_phrase VARCHAR COLLATE 'utf8',
  english_phrase VARCHAR COLLATE 'en',
  spanish_phrase VARCHAR COLLATE 'sp'
  );

INSERT INTO collation_demo (uncollated_phrase, utf8_phrase, english_phrase, spanish_phrase) 
   VALUES ('pinata', 'pinata', 'pinata', 'piñata');
Copy

表中的以下查询显示了预期值:

SELECT * FROM collation_demo;
+-------------------+-------------+----------------+----------------+
| UNCOLLATED_PHRASE | UTF8_PHRASE | ENGLISH_PHRASE | SPANISH_PHRASE |
|-------------------+-------------+----------------+----------------|
| pinata            | pinata      | pinata         | piñata         |
+-------------------+-------------+----------------+----------------+
Copy

以下查询找不到匹配项,因为字符 ñn 不匹配:

SELECT * FROM collation_demo WHERE spanish_phrase = uncollated_phrase;
+-------------------+-------------+----------------+----------------+
| UNCOLLATED_PHRASE | UTF8_PHRASE | ENGLISH_PHRASE | SPANISH_PHRASE |
|-------------------+-------------+----------------+----------------|
+-------------------+-------------+----------------+----------------+
Copy

更改排序规则并不会强制将相关但不相等的字符(例如 ñn)视为相等字符:

SELECT * FROM collation_demo 
    WHERE spanish_phrase = uncollated_phrase COLLATE 'sp';
+-------------------+-------------+----------------+----------------+
| UNCOLLATED_PHRASE | UTF8_PHRASE | ENGLISH_PHRASE | SPANISH_PHRASE |
|-------------------+-------------+----------------+----------------|
+-------------------+-------------+----------------+----------------+
Copy

以下示例演示了排序规则对排序顺序的影响:

INSERT INTO collation_demo (spanish_phrase) VALUES
   ('piña colada'),
   ('Pinatubo (Mount)'),
   ('pint'),
   ('Pinta');
Copy
SELECT spanish_phrase FROM collation_demo 
  ORDER BY spanish_phrase;
+------------------+
| SPANISH_PHRASE   |
|------------------|
| piña colada      |
| piñata           |
| Pinatubo (Mount) |
| pint             |
| Pinta            |
+------------------+
Copy

以下查询将排序规则从“sp”(西班牙语)更改为“utf8”,从而将 ñn 的顺序互换:

SELECT spanish_phrase FROM collation_demo 
  ORDER BY COLLATE(spanish_phrase, 'utf8');
+------------------+
| SPANISH_PHRASE   |
|------------------|
| Pinatubo (Mount) |
| Pinta            |
| pint             |
| piña colada      |
| piñata           |
+------------------+
Copy

此示例显示了如何使用 COLLATION 函数查看表达式(例如列)的排序规则:

CREATE TABLE collation_demo2 (c1 VARCHAR COLLATE 'fr', c2 VARCHAR COLLATE '');
INSERT INTO collation_demo2 (c1, c2) VALUES
    ('a', 'a'),
    ('b', 'b');
Copy
SELECT DISTINCT COLLATION(c1), COLLATION(c2) FROM collation_demo2;
+---------------+---------------+
| COLLATION(C1) | COLLATION(C2) |
|---------------+---------------|
| fr            | NULL          |
+---------------+---------------+
Copy

您还可以使用 DESCRIBE TABLE 查看有关表中各列的排序规则信息:

DESC TABLE collation_demo2;
+------+--------------------------------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------+ 
| name | type                           | kind   | null? | default | primary key | unique key | check | expression | comment | policy name |
|------+--------------------------------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------|
| C1   | VARCHAR(16777216) COLLATE 'fr' | COLUMN | Y     | NULL    | N           | N          | NULL  | NULL       | NULL    | NULL        |
| C2   | VARCHAR(16777216)              | COLUMN | Y     | NULL    | N           | N          | NULL  | NULL       | NULL    | NULL        |
+------+--------------------------------+--------+-------+---------+-------------+------------+-------+------------+---------+-------------+
Copy
语言: 中文