LC_CTYPE 对 PostgreSQL 数据库有什么影响?

Gre*_* D. 34 postgresql collation

所以,我有几个带有 PostgreSQL 的 Debian 服务器。从历史上看,这些服务器和 PostgreSQL 使用拉丁文 9 字符集进行了本地化,当时还可以。现在我们必须处理波兰语、希腊语或中文等问题,因此改变它成为一个日益严重的问题。

当我尝试创建 UTF8 数据库时,收到消息:

错误:编码 UTF8 与语言环境 fr_FR 不匹配 详细信息:所选的 LC_CTYPE 设置需要编码 LATIN9。

有几次我和我的老朋友 Google 对这个主题进行了一些研究,我能找到的只是一些过于复杂的过程,比如更新 Debian LANG、使用正确的字符集重新编译 PostgreSQL、编辑所有LC_系统变量和其他晦涩的解决方案。所以暂时,我们把这个问题放在一边。

最近,它又回来了,希腊人想要这些东西,而拉丁语 9 不想。当我再次研究这个问题时,一位同事冲我说:“不,这很容易,你看。”

他什么也没编辑,也没有做魔术,他只是做这个 SQL 查询:

CREATE DATABASE my_utf8_db
  WITH ENCODING='UTF8'
       OWNER=admin
       TEMPLATE=template0
       LC_COLLATE='C'
       LC_CTYPE='C'
       CONNECTION LIMIT=-1
       TABLESPACE=pg_default;
Run Code Online (Sandbox Code Playgroud)

它工作得很好。

我实际上并不知道,LC_CTYPE='C'而且我很惊讶在 Google 甚至 Stack Overflow 上的第一个解决方案中都没有使用它。我环顾四周,只在 PostgreSQL 文档中找到了一个提及。

当 LC_CTYPE 为 C 或 POSIX 时,允许使用任何字符集,但对于 LC_CTYPE 的其他设置,只有一种字符集可以正常工作。由于 LC_CTYPE 设置被 initdb 冻结,因此在集群的不同数据库中使用不同编码的明显灵活性比实际更具理论性,除非您选择 C ​​或 POSIX 语言环境(从而禁用任何真正的语言环境感知)。

所以这让我想知道,这太容易了,太完美了,有什么缺点?而且我还很难找到答案。所以我来这里发帖:

tl; dr:使用LC_CTYPE='C'特定本地化的缺点是什么?这样做不好吗?我应该期待打破什么?

Dan*_*ité 38

在特定本地化上使用 LC_CTYPE='C' 的缺点是什么

该文档在Locale Support 中提到了语言环境和 SQL 功能之间的关系:

区域设置影响以下 SQL 功能:

  • 使用 ORDER BY 或文本数据的标准比较运算符在查询中排序

  • upper、lower 和 initcap 函数

  • 模式匹配运算符(LIKE、SIMILAR TO 和 POSIX 风格的正则表达式);区域设置影响不区分大小写的匹配和字符类正则表达式对字符的分类

  • to_char 函数族

  • 使用带有 LIKE 子句的索引的能力

第一项(排序顺序)是 about LC_COLLATE,其他的似乎都是 about LC_CTYPE

LC_COLLATE

LC_COLLATE影响字符串之间的比较。在实践中,最明显的效果是排序顺序。LC_COLLATE='C'(或POSIXwhich 是同义词)意味着它是驱动比较的字节顺序,而language_REGION形式中的区域设置意味着文化规则将驱动比较。

从 UTF-8 数据库内部执行的法语名称示例:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
 AS l(firstname)
order by firstname collate "fr_FR";
Run Code Online (Sandbox Code Playgroud)

结果:

 名 
-----------
 比阿特丽斯
 贝雷尼斯
 伯纳德
 鲍里斯

béatrice出现在 之前boris,因为重音 E 与 O 比较,就好像它是非重音一样。这是一个文化规则。

这与C语言环境发生的情况不同:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris')) 
 AS l(firstname)
order by firstname collate "C";
Run Code Online (Sandbox Code Playgroud)

结果:

 名 
-----------
 伯纳德
 鲍里斯
 比阿特丽斯
 贝雷尼斯

现在带重音 E 的名称被推到列表的末尾。éUTF-8 中的字节表示是十六进制的C3 A9,因为o它是6f. 在语言环境下c3大于.6fC'béatrice' > 'boris'

这不仅仅是口音。还有更复杂的规则,包括连字符、标点符号和像œ. 每个地方都会出现奇怪的文化规则。

现在,如果要比较的字符串碰巧混合了不同的语言,例如firstname为来自世界各地的人们设置一个列,那么任何特定的语言环境都不应该占主导地位,无论如何,因为不同语言的不同字母表没有被设计为相互排序。

在这种情况下C是一个理性的选择,它的优点是速度更快,因为没有什么比纯字节比较更胜一筹了。

LC_CTYPE

LC_CTYPE设置为“C”意味着C函数像isupper(c)tolower(c)给预期的结果只在US-ASCII字符范围内(即最高0x7F的代码点以Unicode)。

因为像upper(), lower()or 之类的 SQL 函数initcap 是在这些 libc 函数之上的 Postgres 中实现的,所以一旦字符串中存在非 US-ASCII 字符,它们就会受到此影响。

例子:

test=> show lc_ctype;
  lc_ctype   
-------------
 fr_FR.UTF-8
(1 row)

-- Good result
test=> select initcap('élysée');
 initcap 
---------
 Élysée
(1 row)

-- Wrong result
-- collate "C" is the same as if the db has been created with lc_ctype='C'
test=> select initcap('élysée' collate "C");
 initcap 
---------
 éLyséE
(1 row)
Run Code Online (Sandbox Code Playgroud)

对于C语言环境,é被视为不可分类的字符。

同样使用正则表达式也会得到错误的结果:

test=> select 'élysée' ~ '^\w+$';
 ?column? 
----------
 t
(1 row)

test=> select 'élysée' COLLATE "C" ~ '^\w+$';
 ?column? 
----------
 f
(1 row)
Run Code Online (Sandbox Code Playgroud)

  • 这个答案实际上是针对 LC_COLLATE,而不是 LC_CTYPE。LC_CTYPE 用于确定字符是否为数字、字母、空格、标点等。 (2认同)

caf*_*905 11

参考 Daniel 接受的关于使用排序规则排序的答案,请注意,如果您在 Mac 上运行 PostgreSQL,由于操作系统级别的某些排序规则设置不足,您的首选排序规则可能无法按预期运行。您可以在此处阅读有关该问题的更多信息:

http://www.postgresql.org/message-id/4B4E845F.80906@postnewspapers.com.au

这不是特定于 PostgreSQL 的问题,具体来说,而是 Mac 的排序规则设置的默认配置的问题。我当前的系统在 OS X El Capitan 版本 10.11 上运行 PostgreSQL 9.3 并遇到此问题。无论我使用“fr_FR”还是“en_US”排序规则,我的系统都会返回相同的查询结果。例如:

使用“fr_FR”排序规则:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";

results:
==============
bernard
boris
béatrice
bérénice
Run Code Online (Sandbox Code Playgroud)

使用“en_US”排序规则:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "en_US";

results:
==============
bernard
boris
béatrice
bérénice
Run Code Online (Sandbox Code Playgroud)

在我的系统上,“fr_FR”和“en_US”的排序规则设置(在操作系统级别)与 shell 中通过运行 diff 所演示的相同:

cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE
Run Code Online (Sandbox Code Playgroud)

希望这些附加信息对在遇到此问题的 Mac 上使用 PostgreSQL 的任何人都有帮助。