使用 COLLATE 的不区分大小写的 ORDER BY 子句

Man*_*ngo 5 postgresql collation order-by

我花了很长时间寻找这个,但收到的信息很混乱。

\n

在其他 DBMS(在 SQLite、Oracle、MariaDB、MSSQL 中测试)中,我可以使用以下子句覆盖默认排序顺序COLLATE

\n
SELECT *\nFROM orderby\nORDER BY string COLLATE \xe2\x80\xa6 ;\n\n--  SQLite:     BINARY | NOCASE\n--  MariaDB:    utf8mb4_bin | utf8mb4_general_ci\n--  Oracle:     BINARY | BINARY_CI\n--  MSSQL:      Latin1_General_BIN | Latin1_General_CI_AS\n
Run Code Online (Sandbox Code Playgroud)\n

我仔细阅读了文档并进行了全面的搜索,但我无法\xe2\x80\x99找到任何对于 PostgreSQL 如此简单的东西。

\n

是否有一个COLLATE子句值可以对大小写不敏感进行排序?

\n

我知道有很多关于区分大小写的问题,但是(a)大多数问题都是旧问题,并且(b)我没有看到与该COLLATE条款相关的问题。

\n

FWIW,我正在 PostgreSQL 11.8 上进行测试。我在http://sqlfiddle.com/#!17/05cab/1上有一个测试小提琴,但它\xe2\x80\x99s 仅适用于 PostgreSQL 9.6。

\n

MySQL/MariaDB 和 SQL Server 默认不区分大小写,这在对大多数文本进行排序时肯定有意义。Oracle 和 SQLite 默认区分大小写,但对于不区分大小写的排序有一个相对简单的解决方案。我的数据库的默认排序规则是en_US.UTF-8. 我\xe2\x80\x99m 试图填补这里的一些空白。

\n

Sol*_*zky 4

如果您使用的是 PostgreSQL 12 或更高版本,您将能够通过指定的CREATE COLLATIONdeterministic = false命令创建新的排序规则:

\n
CREATE COLLATION ci (provider = icu, locale = \'en-US-u-ks-level2\', deterministic = false);\n
Run Code Online (Sandbox Code Playgroud)\n

我使用 db<>fiddle 代替 sqlfiddle,它提供了多个版本的 PostgreSQL,例如:

\n

https://dbfiddle.uk/?rdbms=postgres_12&fiddle=a8f4b330d04266947fb11e5c04fa4891&hide=2

\n\n

此解决方案类似于此相关答案(也在 DBA.SE 上)的解决方案:
\n LIKE 不支持 PostgreSQL 不确定性排序规则

\n

带有ORDER BY子句的前四个 db<>fiddle 示例查询显示三种不同的排序结果:

\n
    \n
  1. 查询ORDER BY "string" COLLATE "POSIX"返回的行更接近二进制/序数排序,所有大写字母 (A - Z) 分组在所有小写字母 (a - z) 之前。
  2. \n
  3. ORDER BY "string"查询ORDER BY "string" COLLATE "en_US.utf8"以有效区分大小写的顺序返回行,其中“A”和“a”组合在一起,并且每个字母的小写字母位于同一字母的大写字母之前(aA bB ...)。
  4. \n
  5. ORDER BY "string" COLLATE "ci"查询以真正不区分大小写的顺序返回行,其中“A”和“a”分组在一起,这次每个特定字母的大小写不按预定义的顺序排列,这两种大小写甚至可以在一个字母中混合。由于不能保证每次都会产生相同的顺序,因此以下是我当前看到的此查询的结果集示例:\n
    -8    banana\n-15   Banana\n-9    bANana\n1116  Banana\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
\n

版本12的排序规则支持文档中的以下声明解释了需要设置为:deterministicfalse

\n
\n

请注意,虽然此系统允许创建 \xe2\x80\x9ciignore case\xe2\x80\x9d 或 \xe2\x80\x9cignoreaccents\xe2\x80\x9d 或类似的排序规则(使用 ks 键),但为了进行此类排序规则要以真正不区分大小写或重音的方式操作,还需要在;... 中将它们声明为不确定性CREATE COLLATION,否则,根据排序规则比较相等但字节不相等的任何字符串将根据他们的字节值。

\n
\n

但版本11的同一个排序规则支持文档中的同一条注释解释了排序仍然有效地区分大小写:

\n
\n

请注意,虽然此系统允许创建 \xe2\x80\x9cignore case\xe2\x80\x9d 或 \xe2\x80\x9cignoreaccents\xe2\x80\x9d 或类似的排序规则(使用 ks 键),但 PostgreSQL 并不moment 允许此类排序规则以真正不区分大小写或重音的方式进行操作。任何根据排序规则比较相等但字节不相等的字符串都将根据其字节值进行排序。

\n
\n

仔细查看CREATE COLLATION文档,似乎该deterministic属性(默认为true但需要设置false为进行不敏感的比较和排序)是在Version 12中引入的,这并没有真正帮助您。(@LaurenzAlbe 在评论中推荐了一种可能适用于旧版本 libicu 的替代语法,并调整了上面链接的该语法的 db<>fiddle 测试并在 PostgreSQL 11 上运行,但它似乎不起作用:https: // dbfiddle.uk/?rdbms=postgres_11&fiddle=c3c48e111ed1837987524fee2c54a183&hide=2 )

\n

这给你留下了两个选择:

\n
    \n
  1. lower()功能
  2. \n
  3. citext数据类型(如 @a_horse_with_no_name 在对该问题的评论中提到的)
  4. \n
\n

citext数据类型在内部调用该lower()函数,但具有以下优点(根据上面直接链接的文档):

\n
    \n
  1. 它适用于由UNIQUEPRIMARY KEY约束创建的隐式索引
  2. \n
  3. 不需要记住添加到lower()比较的两边
  4. \n
\n

两个主要考虑因素是:

\n
\n
    \n
  • citext效率不高,text因为运算符函数和 B 树比较函数必须复制数据并将其转换为小写以进行比较。然而,它比使用 lower 获得不区分大小写的匹配稍微更有效。

    \n
  • \n
  • citext如果您需要数据在某些上下文中区分大小写而在其他上下文中不区分大小写进行比较,则没有多大帮助。标准答案是text当需要不区分大小写比较时,使用类型并手动使用下层函数;如果不经常需要不区分大小写的比较,那么这可以正常工作。如果您大多数时候需要不区分大小写的行为,而很少需要区分大小写的行为,请考虑将数据存储为,citext并在需要区分大小写的比较时将列显式转换为文本。在任何一种情况下,如果您希望两种类型的搜索都快速,则需要两个索引。

    \n
  • \n
\n
\n

听起来像是citext创建了数据的小写副本,该副本占用更多空间,但由于预先计算而速度稍快。

\n

另外,citext需要安装。我修改了前面的示例查询来展示citextPostgreSQL 11 上可以做什么,但似乎citext没有安装在 db<>fiddle 上。我将提出一个请求。但这里是测试查询,应该显示它在您的系统上如何工作(如果您已citext安装):

\n

https://dbfiddle.uk/?rdbms=postgres_11&fiddle=7dd95430aad8f646bfa1b7e79e21871c&hide=2

\n

话虽如此,重要的是要注意,对于唯一的或最右边/最后一列,不区分大小写的排序与区分大小写的排序没有太大区别:两种情况下的相同字母将被分组一起。只是在区分大小写的排序中,每个字母中的每个大小写都会组合在一起(即所有“a”之前或之后的所有“A”),而不区分大小写的排序允许“A”和“ a"s 可以混合(因为两者都没有优先级)。当列表中至少有一个附加排序标准(指定在相关字符串列的右侧(即之后))时,允许同一字母的不同大小写混合就变得很重要。本答案顶部链接的 db<>fiddle 中的最后两个示例查询显示了差异。ORDER BY

\n

考虑到所有这些,主要问题是:您到底想要实现什么目标?正如问题中当前所述,该请求是针对区分大小写排序。这是否意味着比较仍应区分大小写?这似乎是一个奇怪的组合,因为敏感比较中进行/想要区分大小写是相当常见的,但排序不太重要,因为区分大小写和不区分大小写的排序之间通常没有什么区别(区分大小写是一致的,但区分大小写的字符串将具有随机排序,仅大小写不同,并且随机性有时会导致与区分大小写返回的顺序相同),除非有其他字段正在排序。

\n

OP 的评论:

\n
\n

MySQL/MariaDB 和 SQL Server 默认不区分大小写,这在对大多数文本进行排序时肯定有意义。Oracle 和 SQLite 默认区分大小写,但对于不区分大小写的排序有一个相对简单的解决方案。我的数据库的默认排序规则是en_US.UTF-8. 我\xe2\x80\x99m 试图填补这里的一些空白。

\n
\n

目前还不清楚为什么需要填补任何“空白”,但听起来术语上有一个小问题:正在使用术语“排序”/“排序”,但我怀疑主要问题是通过比较,考虑到在排序时区分大小写和不区分大小写之间通常没有明显的区别。并且,在不使用子句覆盖谓词或ORDER BY字段/表达式的情况下COLLATE,相同的敏感性(或缺乏敏感性)将应用于两者。这很重要,因为如果我正确地认为 OP 确实意味着两者,那么:

\n
    \n
  1. 就性能而言,我猜想这lower实际上可能比citext 在这个特定上下文中强制转换稍好一些,因为无论如何citext都会调用lower,但也会进行数据类型转换。公平地说,我还没有对此进行任何测试,因此这只是一个经过教育的猜测,假设当用作列的数据类型时,由于citext值是预先计算的,因此稍微好一点,但在这个如果没有机会预先计算。
  2. \n
  3. 在 中使用citext或只能解决一半问题(不太重要的一半,再次考虑到区别几乎不明显)lowerORDER BY
  4. \n
  5. 最有可能的是,您需要停止关注子句ORDER BY并用作citext列的数据类型,这将同时处理比较和排序。
  6. \n
\n