PostgreSQL表筛选西里尔字符行异常:误返回拉丁字符的原因及解决
嘿,这个问题我太熟了!你遇到的情况大概率是这几个原因导致的,咱们慢慢捋:
1. 你看到的“拉丁字符”其实是视觉相似的西里尔字符
很多西里尔字母和拉丁字母长得几乎一模一样,但编码完全不同——比如西里尔的А(U+0410)、В(U+0412)、С(U+0421),看起来和拉丁的A、B、C没区别,但它们刚好落在你指定的\u0410-\u044f范围里。如果这些行里的“拉丁字符”其实是用西里尔键盘输入的这类视觉重复字符,你的查询自然会把它们捞出来。
你可以快速验证:对那些可疑的行,用unicode()函数查看字符的实际编码值,比如:
SELECT title, unicode(substring(title from 1 for 1)) FROM "items" WHERE id = 你的可疑行ID;
如果返回的数值在1040-1103(对应U+0410到U+044f的十进制值),那说明这就是西里尔字符,只是看起来像拉丁而已。
2. SIMILAR TO的Unicode范围匹配有坑
PostgreSQL里的SIMILAR TO是基于SQL标准的正则实现,对Unicode范围的支持远不如POSIX正则(也就是~操作符)可靠。有时候会因为数据库排序规则(collation)的影响,把不在范围内的字符误匹配。
方式一:用POSIX正则的西里尔字符类(推荐)
直接用[:cyrillic:]字符类,这是PostgreSQL官方推荐的匹配西里尔字符的方式,能覆盖所有标准和扩展西里尔字符,还不用记复杂的编码范围:
-- 不区分大小写匹配所有含西里尔字符的行 SELECT * FROM "items" WHERE title ~* '[:cyrillic:]'; -- 严格区分大小写的话用~ SELECT * FROM "items" WHERE title ~ '[:cyrillic:]';
方式二:修正POSIX正则的编码范围
如果你坚持想用编码范围,换成POSIX正则的写法,同时补上扩展西里尔字符的范围(避免漏掉特殊字符):
SELECT * FROM "items" WHERE title ~* '[\u0410-\u044f\u0400-\u040f\u0450-\u045f]';
方式三:检查数据库字符编码
确保你的数据库和表的字符编码是UTF8——如果是LATIN1这类非Unicode编码,PostgreSQL根本无法正确处理Unicode范围的匹配,会出现各种奇怪的结果。你可以用下面的命令检查:
SELECT datname, encoding FROM pg_database WHERE datname = '你的数据库名';
如果不是UTF8,建议先备份数据,再迁移到UTF8编码。
内容的提问来源于stack exchange,提问作者salvo9415

