开发者社区> 德哥> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

PostgreSQL 按拼音排序 - convert to GBK/EUC_CN coding

简介:
+关注继续查看

标签

PostgreSQL , conversion , pg_conversion , 拼音 , 编码转换 , convert , convert_to , convert_from


背景

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

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

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

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

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

编码

PostgreSQL支持的编码如下

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

PostgreSQL Character Sets

Name Description Language Server? Bytes/Char Aliases
BIG5 Big Five Traditional Chinese No 1-2 WIN950, Windows950
EUC_CN Extended UNIX Code-CN Simplified Chinese Yes 1-3 -
EUC_JP Extended UNIX Code-JP Japanese Yes 1-3 -
EUC_JIS_2004 Extended UNIX Code-JP, JIS X 0213 Japanese Yes 1-3 -
EUC_KR Extended UNIX Code-KR Korean Yes 1-3 -
EUC_TW Extended UNIX Code-TW Traditional Chinese, Taiwanese Yes 1-3 -
GB18030 National Standard Chinese No 1-4 -
GBK Extended National Standard Simplified Chinese No 1-2 WIN936, Windows936
ISO_8859_5 ISO 8859-5, ECMA 113 Latin/Cyrillic Yes 1 -
ISO_8859_6 ISO 8859-6, ECMA 114 Latin/Arabic Yes 1 -
ISO_8859_7 ISO 8859-7, ECMA 118 Latin/Greek Yes 1 -
ISO_8859_8 ISO 8859-8, ECMA 121 Latin/Hebrew Yes 1 -
JOHAB JOHAB Korean (Hangul) No 1-3 -
KOI8R KOI8-R Cyrillic (Russian) Yes 1 KOI8
KOI8U KOI8-U Cyrillic (Ukrainian) Yes 1 -
LATIN1 ISO 8859-1, ECMA 94 Western European Yes 1 ISO88591
LATIN2 ISO 8859-2, ECMA 94 Central European Yes 1 ISO88592
LATIN3 ISO 8859-3, ECMA 94 South European Yes 1 ISO88593
LATIN4 ISO 8859-4, ECMA 94 North European Yes 1 ISO88594
LATIN5 ISO 8859-9, ECMA 128 Turkish Yes 1 ISO88599
LATIN6 ISO 8859-10, ECMA 144 Nordic Yes 1 ISO885910
LATIN7 ISO 8859-13 Baltic Yes 1 ISO885913
LATIN8 ISO 8859-14 Celtic Yes 1 ISO885914
LATIN9 ISO 8859-15 LATIN1 with Euro and accents Yes 1 ISO885915
LATIN10 ISO 8859-16, ASRO SR 14111 Romanian Yes 1 ISO885916
MULE_INTERNAL Mule internal code Multilingual Emacs Yes 1-4
SJIS Shift JIS Japanese No 1-2 Mskanji, ShiftJIS, WIN932, Windows932
SHIFT_JIS_2004 Shift JIS, JIS X 0213 Japanese No 1-2 -
SQL_ASCII unspecified (see text) any Yes 1 -
UHC Unified Hangul Code Korean No 1-2 WIN949, Windows949
UTF8 Unicode, 8-bit all Yes 1-4 Unicode
WIN866 Windows CP866 Cyrillic Yes 1 ALT
WIN874 Windows CP874 Thai Yes 1 -
WIN1250 Windows CP1250 Central European Yes 1 -
WIN1251 Windows CP1251 Cyrillic Yes 1 WIN
WIN1252 Windows CP1252 Western European Yes 1 -
WIN1253 Windows CP1253 Greek Yes 1 -
WIN1254 Windows CP1254 Turkish Yes 1 -
WIN1255 Windows CP1255 Hebrew Yes 1 -
WIN1256 Windows CP1256 Arabic Yes 1 -
WIN1257 Windows CP1257 Baltic Yes 1 -
WIN1258 Windows CP1258 Vietnamese Yes 1 ABC, TCVN, TCVN5712, VSCII

与中文编码排序相关的包括 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);
}

参考

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

2. https://www.postgresql.org/docs/9.6/static/sql-createconversion.html

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Qt&Vtk-003-读取jpg、png、dicom等格式图片
本文其实才能算是真正的Qt与Vtk结合,具体实现JPG、PNG、TIFF、DICOM、BMP及一个3D Cube显示。
248 0
PostgreSQL sharding : citus 系列2 - TPC-H
标签 PostgreSQL , citus , tpc-h 背景 紧接着上一篇文档,本文测试citus的tpc-h能力(包括兼容性). 《PostgreSQL sharding : citus 系列1 - 多机部署(含OLTP(TPC-B)测试)》 《(TPC-H测试 SF=10,SF=200) PostgreSQL 11 vs 10 vs Deepgreen》 https://github.com/digoal/gp_tpch 实际测试过程中,发现CITUS对TPC-H的SQL支持并不完整。
1736 0
PostgreSQL 10.1 手册_部分 III. 服务器管理_第 20 章 客户端认证_20.1. pg_hba.conf文件
20.1. pg_hba.conf文件 客户端认证是由一个配置文件(通常名为pg_hba.conf并被存放在数据库集簇目录中)控制(HBA表示基于主机的认证)。在initdb初始化数据目录时,它会安装一个默认的pg_hba.conf文件。
1394 0
Go的CSP并发模型实现:M, P, G
最近抽空研究、整理了一下Golang调度机制,学习了其他大牛的文章。把自己的理解写下来。如有错误,请指正!!!         golang的goroutine机制有点像线程池:        一、go 内部有三个对象: P对象(processor) 代表上下文(或者可以认为是cpu),M(work thread)代表工作线程,G对象(goroutine).        二、正常情况下一个cpu对象启一个工作线程对象,线程去检查并执行goroutine对象。
2486 0
ZigBee TI ZStack CC2530 3.12 LED驱动移植及使用
(配套源码、软件、开发板等资源,可移步博客同名QQ群/TB店铺:拿破仑940911) 一、前言 关于Z-Stack协议栈中的LED控制,如果使用协议栈中自带的驱动,很简单就可以实现非常丰富的功能。
1735 0
+关注
德哥
公益是一辈子的事, I am digoal, just do it.
文章
问答
来源圈子
更多
让用户数据永远在线,让数据无缝的自由流动
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
PostgreSQL 10.1 中文手册
立即下载
认识PostgreSQL中与众不同的索引
立即下载
Oracle 至PostgreSQL案例分享
立即下载