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


25

因此,我几乎没有使用PostgreSQL的Debian服务器。从历史上看,这些服务器和PostgreSQL是使用Latin 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;

而且效果很好。

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

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

因此,让我感到奇怪的是,这太容易了,太完美了,缺点是什么?而且,我很难找到答案。所以我在这里发布:

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

Answers:


25

在特定的本地化环境中使用LC_CTYPE ='C'有什么不利之处

该文档提到了“语言环境支持”中的语言环境和SQL功能之间的关系:

语言环境设置会影响以下SQL功能:

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

  • 上,下和initcap函数

  • 模式匹配运算符(LIKE,SIMILAR TO和POSIX样式的正则表达式);语言环境会影响不区分大小写的匹配和字符类正则表达式对字符的分类

  • to_char系列功能

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

第一项(排序顺序)约为LC_COLLATE,其他项似乎全部约为LC_CTYPE

LC_COLLATE

LC_COLLATE影响字符串之间的比较。实际上,最明显的效果是排序顺序。LC_COLLATE='C'(或POSIX同义词)表示驱动比较的是字节顺序,而language_REGION形式的语言环境表示文化规则将驱动比较。

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

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

结果:

 名字 
-----------
 贝阿特里切
 贝伦尼斯
 伯纳德
 鲍里斯

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";

结果:

 名字 
-----------
 伯纳德
 鲍里斯
 贝阿特里切
 贝伦尼斯

现在,带有重音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)。

由于SQL函数(例如upper()lower()或)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)

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

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

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

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

因此,如果我做对了,即使您制造的是UTF-8服务器,也会出现订单问题?我猜想在UTF-8上设置系统LC_CTYPE或在UTF-8中编译PostgreSQL会导致与您指出的相同的比较问题。
Gregoire D.

为了对此进行扩展,是否可以对查询强制排序,以便比较在本地正确?
Gregoire D.

是的,单个字符串比较可以嵌入自己的整理规则,就像我在答案中使用的collate "C"after一样order by。由您决定应用程序是否需要它以及在何处需要它。那里的大多数应用程序都不在乎。
DanielVérité15年

1
还要注意,各个列的COLLATE说明符可能与数据库的说明符不同。
丹尼尔·维泰

2
这个答案确实适用于LC_COLLATE,而不是LC_CTYPE。LC_CTYPE用于决定一个字符是数字,字母,空格,标点符号等
jjanes

10

关于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

使用“ 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

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

cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE

希望这些附加信息对在Mac上使用PostgreSQL且遭受此问题困扰的任何读者有所帮助。


如何使它在现代Mac中工作。您是否经历了使它在Mac上运行的任何事情?
Dinesh Kumar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.