PostgreSQL 从开发要换PG表字段的 collaion 说起 到 程序员别异想天开了

c9b84179cf99dd5c7d5d6e195885859c.png

2021年绝对是一个有意思的念头,估计过10年都会想到今年的一些变革,很多人都会被影响,改变,重新开始,或寻找新的路径。

归正题,新公司的开发小朋友,对DB 提出了一个问题,就是要修改某个表的字段的collation,究其原因为了某些业务中这个字段的排序。然后我就告诉DB,NO NO NO ,究其原因曾经SQL SERVER 更换collation后产生的问题还在记忆里面。不过PG的collation  说起来还真没有怎么研究,空白要被填补,否则不能一致倚老卖老。

首先要确认几点,

1   PG 要更换collation 针对表的字段,主要就是文本的类型 char ,varchar, text. 

2   collation 与PG的 encoding 有关,于创建数据库INSTANCE 初始化有关,与建库时的建库语句有关,也与建表的语句有关。

3     网上我是没有找到中文关于这类的问题清晰的描述或文章,我就当开个头。

从PG的encoding 说起,PG 的encoding 分为 server encoding 和 client encoding 

通过下面的语句,我们可以找到当前数据库的关于encoding, collation 的信息

SELECT d.datname as "Name",
       pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
       pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
       d.datcollate as "Collate",
       d.datctype as "Ctype",
       pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_database d
ORDER BY 1;

d9b3101d31820375cabc75c5ef02dec6.png

数据库的encoding 是UTF8 ,客户的client encoding 也是UTF8

942ba848a0e61e92f7e53fe9e23e8853.png

问题1  我的client_encoding 和 server_encoding 可以不一样吗?

我们来看看下面的操作,将客户端的encoding 变为 BIG5码, 则在键入一些字符,系统会直接报错,无法确认这些字符是什么。

204b20e0170489ef160c931a6f104ce5.png

而将客户端的encoding 更换为 GBK,我们在查询俄文,也不会报错

488a19735178d847dc7f0fa3f8b49c14.png

这就涉及到PG 本身支持的字符编码类型,这里台湾省的BIG5码不在支持范围(以PG13为例),中国的GBK属于PG的扩展标准。  下方是一个表,这个表里面server ICU 为NO 的是 PG 目前不支持的字符编码。

而UTF8 为什么为一个大多数PG的字符的标准,因为这个编码是 ALL,也就是通吃各种字符以及国别的语言。

9b29d4b0f35cee73da81b6be55df3853.png

而这些是怎么带入到我们的建库语句中,参见下图, create database encoding 以及 LC_COLLATE, LC_CTYPE 这三个选项。

59638c21f236d8d2783a83e4db48247b.png

en-coding 是字符的编码集,此数据库下的所有的默认表的设置,以及字段的编码都为UTF8 , lc_collate 为本地化字符排序规则,lc_ctype 为字符集中的字符分类,这里统计了一下关于UTF8的类型的字符分类,在PG14中有262个。

e8bef0aadbcc7835b170663cc4be61f3.png

我们创建一个使用collate 为 zh_CN的数据库,对比 en_US的数据库又什么不同

7fd0c99267fb29d0c940c34fdf170e11.png

我们在两个数据库中的同样的表,中插入同样的数据,然后order by 一下

en utf8

aff86f45c0fec90c07ed9ac1de3a94a1.png

zh utf8

360c66386014e0e66dd08b38c3f83f4d.png

那么此时差距就有了,同样都是一样的字符,在排序后,同样都使用UTF8 ,而在collate 不同的情况下, 默认的name  进行排序后的结果就不一致了。

那么怎么能让两个表的order by 的结果一致。

ALTER TABLE test ALTER COLUMN name SET DATA TYPE character varying(255) COLLATE "en_US";

71ef5921abe0dc7c954b572049046ce4.png

通过修改表的字段的collation后,再次进行order by ,两个表的结果一致了。

说到这里,回到上面的问题,开发要改字段的collation,为什么没有让他改。

原因

1   不确认修改的字段是否会引起索引的重建的问题

2   修改后,部分查询会出现无法进行的情况

975f712b045d067c36fe41be3db5ce80.png

a19fd161d38b556ce490f5f8df1455a8.png

上面两个图是字段的collation 是一致的情况下,可以查询出数据(下面的图),如果我修改了某个表的collation ,则查询直接报错。

2021-09-02 01:18:51.116 EDT [48166] ERROR:  could not determine which collation to use for string hashing
2021-09-02 01:18:51.116 EDT [48166] HINT:  Use the COLLATE clause to set the collation explicitly.
2021-09-02 01:18:51.116 EDT [48166] STATEMENT:  select t.id  from test as t inner join test_2 as t2 on t.name = t2.name order by t.name;
ERROR:  could not determine which collation to use for string hashing
HINT:  Use the COLLATE clause to set the collation explicitly.
test_zh=#

主要的原因还是表join 的情况下,使用到了hash 算法,而人类看上去明明一样的两个表的 两个字段,在你修改了collation后,那就彻彻底底的不一样了。

到此为止,还应该深入,但想睡觉了,不过很想和那个程序员说, 您还是别异想天开了,我们应该有其他的方法解决你的需求。

23574ab3a91763d450faf7a3cc94cf9c.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值