【Oracle字符集】乱码的产生原理

编辑手记:很多人在数据库各种操作中遭遇过乱码的问题,今天我们分析一下乱码产生的原因。本文节选于《循序渐进Oracle》

通常在现实环境中,存在3个字符集设置:

  • 客户端应用字符集(Client ApplicationCharacter Set);
  • 客户端NLS_LANG参数设置;
  • 服务器端,数据库字符集(Character Set)设置。

由于一个字符在客户端应用(如SQLPLUS、CMD、NOTEPAD等)中以怎样的字符显示取决于客户端操作系统,客户端能够显示怎样的字符,我们就可以在应用中录入这些字符。至于这些字符能否在数据库中正常存储,就和另外的两个字符集设置紧密相关了(通常我们可以忽略应用程序的字符集,这个字符集在应用程序安装时,已经被内在的决定,并且会依据操作系统的相关设置进行选择)。

在传输过程中,客户端NLS_LANG主要用于进行转换判断。如果NLS_LANG等于数据库字符集,则不进行任何转换直接把字符插入数据库;如果不同则进行转换,转换主要有两个任务:

  • 如果存在对应关系,则把相应二进制编码经过映射后(这一步映射以后,所代表的字符可能发生转换)传递给数据库。
  • 如果不存在对应关系,则传递一个替换字符(不同平台的替换字符各不相同,最常见的替换字符是“?”)。

数据库字符集,在和客户端NLS_LANG不同时,会对经过NLS_LANG转换的字符进一步处理:对于?(即不存在对应关系的字符)直接以?形式存放入数据库,对于其他字符,在NLS_LANG和数据库字符集之间进行转换后存入。

下面来看一下最为常见的字符集及乱码的产生。

NLS_LANG字符集与数据库字符集不同

当NLS_LANG字符集与数据库字符集不同,且NLS_LANG不同于客户端字符集设置时,存在以下两种可能。

1、客户端输入的字符在NLS_LANG中没有对应的字符,这时无法转换,NLS_LANG使用替换字符替代这些无法映射的字符(这一步转换在TTS中完成),在很多字符集中这个替代字符就是“?”。

2、当客户端的字符在NLS_LANG中对应了不同的字符时,传递给数据库以后发生转换,存储的是字符,但是已经丢失了元数据,数据库中的字符不再代表客户端的输入。而且这个过程不可逆,这也就是为什么很多时候在客户端输入的是正常的编码,而查询之后会得到未知字符的原因。

下面通过下图来简单说明一下这个过程。

当客户端在WE8ISO8859P15字符集时,输入欧元符号€,这时客户端NLS_LANG和数据库端字符集不同,进行第一次转换,客户端€符号编码是A4,在NLS_LANG转换时,A4对应了NLS_LANG中的“¤”,这一步的转换产生了错误映射。由于数据库字符集不同于NLS_LANG设置,这时进一步的转换发生了,存入数据库的编码变成了C2A4,虽然同NLS_LANG进行了正确的转换,但是客户端录入的数据已经损坏或者丢失了。

可以用我们熟悉的字符集做一个简单的测试(测试环境是客户端代码点对应中文18030字符集,NLS_LANG设置为US7ASCII字符集,数据库CHARACTER SET为ZHS16GBK)。

这时发现,查询出来的是混乱的字符,把这些字符转换为二进制就是:

110010 1100010 1001010 1010100

补全8位就是如下序列:

00110010 01100010 01001010 01010100

我们把首位换成1,得到如下序列:

10110010 11100010 11001010 11010100

接下来看正确的存储格式:

把这个结果转换为二进制表示:

10110010 11100010 11001010 11010100

这个结果正是前面乱码首位补全1后的结果。这个测试说明在US7ASCII转换中文的时候除去了首位的1,这样就丢失了元数据,导致乱码出现,NLS_LANG的转换作用由此可加一斑!

NLS_LANG和数据库字符集相同时

在这种情况下,数据库端对客户端传递过来的编码不进行任何转换(这样可以提高性能),直接存储进入数据库,那么这时候就存在和上面同样的问题,如果客户端传递过来的字符集在数据库中有正确的对应就可以正确存储,如果没有,就会被替换字符置换成,乱码就这样产生了。

如下图所示,当NLS_LANG和数据库字符集设置相同都为UTF8时,客户端的欧元符号的编码A4就不会经过任何转换就插入到数据库中,而在UTF8的数据库中,A4代表的是一个非法字符。

来看一个简单的测试(测试环境是客户端代码点对应中文18030字符集,客户端NLS_LANG为US7ASCII,数据库字符集为US7ASCII)。

我们知道这个时候存入的数据,数据库不进行任何转换,在以下的测试中,看到中文在US7ASCII字符集下得以正确显示。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2017-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术之路

sqlserver 各种判断是否存在(表名、函数、存储过程.......)

库是否存在 if exists(select * from master..sysdatabases where name=N'库名') print 'exi...

17710
来自专栏用户画像

sql sever 索引

微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,...

584
来自专栏数据和云

90%的面试者都不知道这道题的答案

小编寄语 亲爱的DBA同胞们,你们是否记得在你找工作时,印象最深刻的面试题呢?那些看似简单的题目,实则蕴藏很大的玄机。今天我们通过一道经典的 ORacle DB...

2706
来自专栏FreeBuf

移位溢注:告别靠人品的偏移注入

在Access数据库类型注入的时候,我们获取不到列名(前提是有表名),一般会选择使用偏移注入,但是这种注入方式往往借助的是个人的人品,且步骤繁琐。本文中我们研究...

1976
来自专栏xingoo, 一个梦想做发明家的程序员

Java程序员的日常——存储过程知识普及

存储过程是保存可以接受或返回用户提供参数的SQL语句集合。在日常的使用中,经常会遇到复杂的业务逻辑和对数据库的操作,使用存储过程可以进行封装。可以在数据库中定...

1748
来自专栏Rgc

mysql数据库优化(二)

https://www.cnblogs.com/sevck/p/6733702.html

892
来自专栏乐沙弥的世界

MySQL数据类型 -- 数值型

版权声明:本文为博主原创文章,欢迎扩散,扩散请务必注明出处。 https://blog.csdn.net/robinson_0612/art...

793
来自专栏沃趣科技

MySQL的一个表最多可以有多少个字段

问题由来 引用我们客户的原话: *创建如下表,提示我:* ? *如果我将下面表中的varchar(200),修改成text(或blob):报错变为另一个:* ?...

5299
来自专栏乐沙弥的世界

Oracle 硬解析与软解析

Oracle 硬解析与软解析是我们经常遇到的问题,什么情况会产生硬解析,什么情况产生软解析,又当如何避免硬解析?下面的描述将给出

693
来自专栏数据和云

Oracle 12.2 - 启用数据库对象的In-Memory转换填充

所谓数据库的列式转换填充,就是数据库从磁盘读取现有的行格式数据,将其转换为列格式,然后再存储到IM列存储中的过程。将数据库对象填充到列式存储会极大地提高访问效率...

3104

扫码关注云+社区