热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PostgreSQL按拼音排序converttoGBK/EUC_CNcoding

digoal德哥专栏PostgreSQL按拼音排序-converttoGBKEU
TAG 15

作者

digoal


日期

2016-12-05


标签

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

}
```


使用字段或排序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 zh_CN.utf8";
QUERY PLAN



Index Only Scan using idxa on a (cost=0.15..31.55 rows=1360 s github - 公益是一辈子的事.

digoal's wechat


推荐阅读
  • 本文深入探讨了数据库性能优化与管理策略,通过实例分析和理论研究,详细阐述了如何有效提升数据库系统的响应速度和处理能力。文章首先介绍了数据库性能优化的基本原则和常用技术,包括索引优化、查询优化和存储管理等。接着,结合实际应用场景,讨论了如何利用容器化技术(如Docker)来部署和管理数据库,以提高系统的可扩展性和稳定性。最后,文章还提供了具体的配置示例和最佳实践,帮助读者在实际工作中更好地应用这些策略。 ... [详细]
  • 本文深入探讨了 MXOTDLL.dll 在 C# 环境中的应用与优化策略。针对近期公司从某生物技术供应商采购的指纹识别设备,该设备提供的 DLL 文件是用 C 语言编写的。为了更好地集成到现有的 C# 系统中,我们对原生的 C 语言 DLL 进行了封装,并利用 C# 的互操作性功能实现了高效调用。此外,文章还详细分析了在实际应用中可能遇到的性能瓶颈,并提出了一系列优化措施,以确保系统的稳定性和高效运行。 ... [详细]
  • Spring Batch 异常处理与任务限制优化策略 ... [详细]
  • 如何使用 net.sf.extjwnl.data.Word 类及其代码示例详解 ... [详细]
  • 我正在使用 Ruby on Rails 构建个人网站。总体而言,RoR 是一个非常出色的工具,它提供了丰富的功能和灵活性,使得创建自定义页面变得既高效又便捷。通过利用其强大的框架和模块化设计,我可以轻松实现复杂的功能,同时保持代码的整洁和可维护性。此外,Rails 的社区支持也非常强大,为开发过程中遇到的问题提供了丰富的资源和解决方案。 ... [详细]
  • 本文深入探讨了 iOS 开发中 `int`、`NSInteger`、`NSUInteger` 和 `NSNumber` 的应用与区别。首先,我们将详细介绍 `NSNumber` 类型,该类用于封装基本数据类型,如整数、浮点数等,使其能够在 Objective-C 的集合类中使用。通过分析这些类型的特性和应用场景,帮助开发者更好地理解和选择合适的数据类型,提高代码的健壮性和可维护性。苹果官方文档提供了更多详细信息,可供进一步参考。 ... [详细]
  • 计算 n 叉树中各节点子树的叶节点数量分析 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 使用 MyEclipse 和 TestNG 测试框架在 Java 中高效进行单元测试
    通过MyEclipse集成TestNG测试框架,可以在Java开发中高效地进行单元测试。本文介绍了在JDK 1.8.0_121和MyEclipse 10.0离线环境下配置和使用TestNG的具体步骤,帮助开发者提高测试效率和代码质量。 ... [详细]
  • Java 中优先级队列的轮询方法详解与应用 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • BZOJ1034 详细解析与算法优化
    本文深入解析了BZOJ1034问题,并提出了优化算法。通过借鉴广义田忌赛马的贪心策略,当己方当前最弱的马优于对方最弱的马时进行匹配;同样地,若己方当前最强的马优于对方最强的马,也进行匹配。此方法在保证胜率的同时,有效提升了算法效率。 ... [详细]
  • 如何在 Java LinkedHashMap 中高效地提取首个或末尾的键值对? ... [详细]
  • NOI题库(noi.openjudge.cn):1.7 编程基础之字符串 T31 至 T35 详解与解析
    T31至T35题目详细解析了字符串处理的基础编程技巧。其中,T31涉及P型编码,要求将一个仅包含数字字符的字符串转换为特定格式的编码串。例如,输入字符串“111223”应输出相应的P型编码结果。其他题目则涵盖了字符串的多种操作和变换方法,包括但不限于子串提取、字符替换和模式匹配等,旨在提升编程者对字符串处理的综合能力。 ... [详细]
  • 如何在Android应用中设计和实现专业的启动欢迎界面(Splash Screen)
    在Android应用开发中,设计与实现一个专业的启动欢迎界面(Splash Screen)至关重要。尽管Android设计指南对使用Splash Screen的态度存在争议,但一个精心设计的启动界面不仅能提升用户体验,还能增强品牌识别度。本文将探讨如何在遵循最佳实践的同时,通过技术手段实现既美观又高效的启动欢迎界面,包括加载动画、过渡效果以及性能优化等方面。 ... [详细]
author-avatar
乱七八糟的孤岛_217
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有