背景

国内的应用,在文本排序上基本都是按照拼音来进行排序的。

在不同的字符集中,汉字的编码可能不一样,比如UTF8和GBK,其中GBK是按拼音的顺序进行编码的,而UTF8则不是。

所以如果你的数据库使用了UTF8编码,对中文字段进行排序时,可能得到的并不是按拼音排序的结果。

在PostgreSQL中,中文按拼音排序的编码包括GB18030, EUC_CN, GBK, BIG5, EUC_TW 等。

为了得到拼音排序,可以使用编码转换后的值来排序,索引也可以使用编码转换的表达式索引。

编码

PostgreSQL支持的编码如下

https://www.postgresql.org/docs/9.6/static/multibyte.html

与中文编码排序相关的包括 GB18030, EUC_CN, GBK, BIG5, EUC_TW

简体常用的包括GBK, EUC_CN。

编码转换

在PostgreSQL中,如果要将字符从一个编码转换为另一个编码,需要告诉数据库(create conversion)怎么转换(使用什么C函数),PG内置了一些转换的C函数和转换方法。

https://www.postgresql.org/docs/9.6/static/catalog-pg-conversion.html

pg_conversion

Name Type References Description
oid oid - Row identifier (hidden attribute; must be explicitly selected)
conname name - Conversion name (unique within a namespace)
connamespace oid pg_namespace.oid The OID of the namespace that contains this conversion
conowner oid pg_authid.oid Owner of the conversion
conforencoding int4 - Source encoding ID
contoencoding int4 - Destination encoding ID
conproc regproc pg_proc.oid Conversion procedure
condefault bool - True if this is the default conversion

查看内置的转换方法

可以看到utf8转换为中文编码的都支持了

postgres=> select * from pg_conversion where conname ~* 'gbk|gb18|euc_cn|euc_tw|big5' order by 1;
conname | connamespace | conowner | conforencoding | contoencoding | conproc | condefault
-----------------+--------------+----------+----------------+---------------+-----------------+------------
big5_to_euc_tw | 11 | 10 | 36 | 4 | big5_to_euc_tw | t
big5_to_mic | 11 | 10 | 36 | 7 | big5_to_mic | t
big5_to_utf8 | 11 | 10 | 36 | 6 | big5_to_utf8 | t
euc_cn_to_mic | 11 | 10 | 2 | 7 | euc_cn_to_mic | t
euc_cn_to_utf8 | 11 | 10 | 2 | 6 | euc_cn_to_utf8 | t
euc_tw_to_big5 | 11 | 10 | 4 | 36 | euc_tw_to_big5 | t
euc_tw_to_mic | 11 | 10 | 4 | 7 | euc_tw_to_mic | t
euc_tw_to_utf8 | 11 | 10 | 4 | 6 | euc_tw_to_utf8 | t
gb18030_to_utf8 | 11 | 10 | 39 | 6 | gb18030_to_utf8 | t
gbk_to_utf8 | 11 | 10 | 37 | 6 | gbk_to_utf8 | t
mic_to_big5 | 11 | 10 | 7 | 36 | mic_to_big5 | t
mic_to_euc_cn | 11 | 10 | 7 | 2 | mic_to_euc_cn | t
mic_to_euc_tw | 11 | 10 | 7 | 4 | mic_to_euc_tw | t
utf8_to_big5 | 11 | 10 | 6 | 36 | utf8_to_big5 | t
utf8_to_euc_cn | 11 | 10 | 6 | 2 | utf8_to_euc_cn | t
utf8_to_euc_tw | 11 | 10 | 6 | 4 | utf8_to_euc_tw | t
utf8_to_gb18030 | 11 | 10 | 6 | 39 | utf8_to_gb18030 | t
utf8_to_gbk | 11 | 10 | 6 | 37 | utf8_to_gbk | t
(18 rows)

编码转换函数

注意数据库版本

PostgreSQL 8.x(如Greenplum), 将字符串从原编码转换为指定编码的字符串返回。

可能存在显示的问题。

                              List of functions
Schema | Name | Result data type | Argument data types | Type
------------+---------------+------------------+---------------------+--------
pg_catalog | convert | text | text, name | normal
pg_catalog | convert | text | text, name, name | normal

PostgreSQL 9.x, 将源编码字符串的字节流转换为指定编码的字符串的字节流返回。

避免了显示的问题。

                              List of functions
Schema | Name | Result data type | Argument data types | Type
------------+--------------+------------------+---------------------+--------
pg_catalog | convert | bytea | bytea, name, name | normal
pg_catalog | convert_from | text | bytea, name | normal
pg_catalog | convert_to | bytea | text, name | normal

如果8.x需要避免显示问题,返回字节流,可以这样使用,推荐使用。

byteain(textout(convert(字符,'源编码','目标编码')))  

例子

当前数据库编码为UTF-8,中文排序未按拼音排序。

postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+---------+-------+-----------------------
db0 | postgres | UTF8 | C | C |
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows) postgres=# select * from (values ('*德华'), ('***')) t(id) order by id;
id
--------
***
*德华
(2 rows)

按拼音排序方法(目标可以改成EUC_CN)

8.x

postgres=> select * from (values ('*德华'), ('***')) t(id) order by byteain(textout(convert(id,'UTF-8','GBK')));
id
--------
*德华
***
(2 rows) 9.x
postgres=# select * from (values ('*德华'), ('***')) t(id) order by convert(id::bytea,'UTF-8','GBK');
id
--------
*德华
***
(2 rows)

注意多音字

中文有一些多音字,比如重庆(chongqing), 但是编码时它可能是按zhong编码的,所以看这个例子。

postgres=> select * from (values ('中山'), ('重庆')) t(id) order by byteain(textout(convert(id,'UTF-8','GBK')));
id
------
中山
重庆
(2 rows)

索引

表达式索引即可,使用immutable function.

代码

8.x

postgres=> \df+ convert
List of functions
Schema | Name | Result data type | Argument data types | Type | Data access | Volatility | Owner | Language | Source code | Description
------------+---------+------------------+---------------------+--------+-------------+------------+-----------+----------+-------------+---------------------------------------------------------
pg_catalog | convert | text | text, name | normal | no sql | stable | xxx | internal | pg_convert | convert string with specified destination encoding name
pg_catalog | convert | text | text, name, name | normal | no sql | stable | xxx | internal | pg_convert2 | convert string with specified encoding names
(2 rows) 9.x postgres=# \df+ convert
List of functions
Schema | Name | Result data type | Argument data types | Type | Volatility | Parallel | Owner | Security | Access privileges | Language | Source code | Description
------------+---------+------------------+---------------------+--------+------------+----------+----------+----------+-------------------+----------+-------------+----------------------------------------------
pg_catalog | convert | bytea | bytea, name, name | normal | stable | safe | postgres | invoker | | internal | pg_convert | convert string with specified encoding names
(1 row)

src/backend/utils/mb/mbutils.c

/*
* Convert string between two arbitrary encodings.
*
* BYTEA convert(BYTEA string, NAME src_encoding_name, NAME dest_encoding_name)
*/
Datum
pg_convert(PG_FUNCTION_ARGS)
{
bytea *string = PG_GETARG_BYTEA_PP(0);
char *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
int src_encoding = pg_char_to_encoding(src_encoding_name);
char *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
int dest_encoding = pg_char_to_encoding(dest_encoding_name);
const char *src_str;
char *dest_str;
bytea *retval;
int len; if (src_encoding < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid source encoding name \"%s\"",
src_encoding_name)));
if (dest_encoding < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid destination encoding name \"%s\"",
dest_encoding_name))); /* make sure that source string is valid */
len = VARSIZE_ANY_EXHDR(string);
src_str = VARDATA_ANY(string);
pg_verify_mbstr_len(src_encoding, src_str, len, false); /* perform conversion */
dest_str = (char *) pg_do_encoding_conversion((unsigned char *) src_str,
len,
src_encoding,
dest_encoding); /* update len if conversion actually happened */
if (dest_str != src_str)
len = strlen(dest_str); /*
* build bytea data type structure.
*/
retval = (bytea *) palloc(len + VARHDRSZ);
SET_VARSIZE(retval, len + VARHDRSZ);
memcpy(VARDATA(retval), dest_str, len); if (dest_str != src_str)
pfree(dest_str); /* free memory if allocated by the toaster */
PG_FREE_IF_COPY(string, 0); PG_RETURN_BYTEA_P(retval);
}

使用字段或排序collate语法纠正排序顺序

使用binary存储格式排序,只能通过编码来修正排序顺序。

除此之外,我们还可以在不改编码的情况下,使用字段或者order by的collate语法来修正排序顺序。

例子

select * from pg_collation ;

设置列级collate
create table a (c1 text collate "zh_CN.utf8"); 修改列collate,会导致rewrite table
alter table a alter c1 type text COLLATE "zh_CN.utf8"; 设置排序级collate
test=# select * from a order by c1 collate "C";
c1
--------
***
*德华
(2 rows)
test=# select * from a order by c1 collate "zh_CN.utf8";
c1
--------
*德华
***
(2 rows) 设置operator collate
test=# select * from a where c1 > '***' collate "C";
c1
--------
*德华
(1 row)
test=# select * from a where c1 > '***' collate "zh_CN.utf8";
c1
----
(0 rows) 设置库级collate
postgres=# create database test with template template0 encoding 'UTF8' lc_collate 'zh_CN.utf8';
postgres=# \c test
You are now connected to database "test" as user "postgres".
test=# create table a (c1 text);
CREATE TABLE
test=# insert into a values ('*德华'),('***');
INSERT 0 2
test=# select * from a order by c1;
c1
--------
*德华
***
(2 rows) 注意索引和创建索引时的collate必须一致,才能使用该索引
postgres=# create table a(c1 text);
CREATE TABLE
postgres=# create index idxa on a(c1 collate "zh_CN.utf8");
CREATE INDEX
postgres=# explain select * from a order by c1;
QUERY PLAN
----------------------------------------------------------------
Sort (cost=10000000094.38..10000000097.78 rows=1360 width=32)
Sort Key: c1
-> Seq Scan on a (cost=0.00..23.60 rows=1360 width=32)
(3 rows) postgres=# explain select * from a order by c1 collate "zh_CN.utf8";
QUERY PLAN
------------------------------------------------------------------------
Index Only Scan using idxa on a (cost=0.15..31.55 rows=1360 width=64)
(1 row)

PostgreSQL 按拼音排序 - convert to GBK/EUC_CN coding的更多相关文章

  1. PostgreSQL下,对汉字按拼音排序

    参考学习此文: http://blog.163.com/digoal@126/blog/static/163877040201173003547236/ 建库 postgres=# \l List o ...

  2. PostgreSQL对汉字按拼音排序

    转自:https://www.cnblogs.com/gaojian/p/3188609.html postgres=# \l List of databases Name | Owner | Enc ...

  3. MySQL按照汉字的拼音排序

    按照汉字的拼音排序,用的比较多是在人名的排序中,按照姓氏的拼音字母,从A到Z排序: 如果存储姓名的字段采用的是GBK字符集,那就好办了,因为GBK内码编码时本身就采用了拼音排序的方法(常用一级汉字37 ...

  4. MySQL按照汉字的拼音排序,mysql汉字排序

    按照汉字的拼音排序,用的比较多是在人名的排序中,按照姓氏的拼音字母,从A到Z排序: 如果存储姓名的字段采用的是GBK字符集,那就好办了,因为GBK内码编码时本身就采用了拼音排序的方法(常用一级汉字37 ...

  5. ecshop添加商品选择品牌时如何按拼音排序

    ECSHOP后台添加新商品时,有一个选择品牌的下拉框,如果品牌太多,在下拉框里查找起来很不方便. 我想给“下拉框里的品牌列表”按品牌名的拼音排序,比如有“中国水利出版社” “中国人民出版社” 这两个品 ...

  6. MySQL按照汉字的拼音排序(转)

    按照汉字的拼音排序,用的比较多是在人名的排序中,按照姓氏的拼音字母,从A到Z排序: 如果存储姓名的字段采用的是GBK字符集,那就好办了,因为GBK内码编码时本身就采用了拼音排序的方法(常用一级汉字37 ...

  7. PHP 字符串数组按照拼音排序的问题

    拼音排序的规则: 字符串包括特殊字符.数字.英文字符.中文字符等等,排序结果要求,特殊字符排在第一梯队,将其按照首个字符ascii码表进行排序,数字字符排在第二梯队,将首个字符数字按照数字大小排序,英 ...

  8. MySQL按照汉字的拼音排序、按照首字母分类

    项目中有时候需要按照汉字的拼音排序,比如联系人列表.矿物分类等,有的还需要按拼音字母从A到Z分类显示. 如果存储汉字的字段编码使用的是GBK字符集,因为GBK内码编码时本身就采用了拼音排序的方法(常用 ...

  9. sqlalchemy & python & datatables & javascript 中文拼音排序

    近期有中文拼单排序需要,查询资料,mysql数据库有convert函数支持 select cname from channel order by convert(cname using gbk); # ...

  10. TP5 按照汉字的拼音排序

    业务需求:接口返回一个列表,但是这个列表要求按一定的条件排序,条件如下: 1,某字段(field1)为null的排前面 2,某字段(field2)为null的排前面 3,姓名(field3)按照汉字的 ...

随机推荐

  1. PyQt4编写界面的两种方式

    PyQt4编写界面的两种方式 应用PyQt4开发图形化界面有两种方式,一种是直接通过QtDesigner通过提供的窗口部件拖拽进行GUI创建,另外一种是直接进行编程实现. 第一种,QtDesigner ...

  2. Nmap常用方法

    1.扫描单个目标地址  在Nmap后面直接添加目标地址即可扫描  nmap 目标地址  2.扫描多个目标地址  如果目标不在同一网段,或在同一网段但不连续且数量不多,可以使用该方法进行扫描  nmap ...

  3. Redis基本操作(2)

    一.list类型 列表的元素类型为string 按照插⼊顺序排序 增加.修改 例1:在左侧插⼊数据 lpush key value1 value2 ... 例2:在右侧插⼊数据 rpush key v ...

  4. 一款极简的流媒体Web服务器(Streaming Media Web Server),提供视频音乐的在线播放功能

    一款极简的流媒体Web服务器(Streaming Media Web Server),提供视频音乐的在线播放功能 A extremely simple web server of streaming ...

  5. WCH沁恒 CH37系列芯片选型以及常见问题的处理(CH376/CH378)

    选型 型号 接口 功能 备注 电源 CH372 并口 USB_Device 全速 USB 设备接口,兼容 USB V2.0 3.1-3.6 4.2-5.4 CH374 SPI/并口 Host/Devi ...

  6. [python]《Python编程快速上手:让繁琐工作自动化》学习笔记2

    1. 读写文件笔记(第8章)(代码下载) 1.1 文件与文件路径 通过import os调用os模块操作目录,常用函数如下: 函数 用途 os.getcwd() 取得当前工作路径 os.chdir() ...

  7. 02安装一个最小化的Hadoop

    安装一个最小化的Hadoop 为了学习HDFS和之后的MapReduce,我们需要安装一个Hadoop. Hadoop一共有3种运行模式 独立模式:不启动守护进程,所有程序运行在一个JVM进程中.独立 ...

  8. angular父子组件传值,子组件传值给父组件,父组件又传值给子组件

  9. 动力节点——day01

    eclipse的快捷键: 1.ctrl+d删除一行 2.ctrl+1进行纠错 3.alt+/自动补全 4.单行注释ctrl+/ 5.多行注释ctrl+shift+/ 取消ctrl+shift+\ 6. ...

  10. 一文搞懂工作流审批(Java+activiti)快速开发+自定义工作流配置

    前言 activiti工作流引擎项目,企业erp.oa.hr.crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求. 一.项目形式 springboot+v ...