[Oracle] 彻底搞懂Oracle字符集

基本概念
字符集(Character set):
是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。常见的字符集有ASCII,ZHS16GB231280,ZHS16GBK等。

字符编码(Character Encoding):
是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其它的一个集合(如电脑编码)进行配对。即在符号集合与数字系统之间建立对应关系。与字符集相对应,常见的字符编码有:ASCii,ZHS16GBK,ZHT16BIG5,ZHS32GB18030等。
字符集的定义其实就是字符的集合,而字符编码则是指怎么将这些字符变成字节用于保存、读取和传输。

万国码(Unicode):包含了几乎人类所有可用的字符,每年还在不断的增加,可以看作是一种通用的字符集。它将全世界所有的字符统一化,统一编码,不会再出现字符不兼容和字符转换的问题。
它有以下三种编码方式:
1.UTF-32编码:
固定使用4个字节来表示一个字符,存在空间利用效率的问题。
2.UTF-16编码:对相对常用的60000余个字符使用两个字节进行编码,其余的使用4字节。
3.UTF- 8编码:兼容ASCII编码;拉丁文、希腊文等使用两个字节;包括汉字在内的其它常用字符使用三个字节;剩下的极少使用的字符使用四个字节。

Oracle字符集基本原理
在搞懂Oracle字符集基本原理之前,一定要先分清以下三个概念:
1. Oracle数据库服务器字符集:即Oracle以哪种字符编码存储字符,可以通过以下语句查出数据库字符集的设置。


代码如下:

SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';
PARAMETER                      VALUE
------------------------------ -----------------
NLS_CHARACTERSET               AL32UTF8

2. 客户端操作系统字符集:即客户端操作系统以哪种字符编码存储字符。
如果是Windows,可以使用chcp命令获得代码页(code page):


代码如下:

C:\Users\xianzhu>chcp
Active code page: 936

根据该代码页,到微软的官方文档《National Language Support (NLS) API Reference》找到其对应的字符集。
如果是Linux,字符集在/etc/sysconfig/i18n设置:


代码如下:

LANG="zh_CN.GB2312" (指定当前操作系统的字符集)
SUPPORTED="zh_CN.GB2312"(指定当前操作系统支持的字符集)
SYSFONT="lat0-sun16"(指定当前操作系统的字体)

3. 客户端NLS_LANG参数:该参数用于向Oracle指示客户端操作系统的字符集。
有了以上3个基本概念之后,我来阐述一下Oracle字符集转换的基本原则:
1.设置客户端的NLS_LANG为客户端操作系统的字符集
2.如果数据库字符集等于NLS_LANG,数据库和客户端传输字符时不作任何转换
3.如果它们俩不等,则需要在不同字符集间转换,只有客户端操作系统字符集是数据库字符集子集的基础上才能正确转换,否则会出现乱码。
几种常见情况分析
下面先看一个例子,再透过现象看本质,我们会针对这个例子进行分析。
该例子如下:


代码如下:

1. 数据库字符集为Unicode(UTF-8编码)
我们的数据库版本是10.2.0.4.0,数据库字符集是:
SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';
PARAMETER                                VALUE
---------------------------------------- ------------------------------
NLS_CHARACTERSET               AL32UTF8
2. 客户端操作系统字符集为代码页936(字符集为ZHS16GBK)
可以使用chcp获得windows的代码页(code page)
C:\Documents and Settings\a105024\Desktop>chcp
Active code page: 936
3. 创建测试表
SQL> create table test(id number,var varchar2(30));
Table created.
4. 插入数据
这里在同一个操作系统启动两个session,session1的NLS_LANG设为和数据库字符集一样(即AL32UTF8):
C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.AL32UTF8
连接数据库并插入一条数据:
Session_1>insert into test values(1,'中国');
1 row created.
Session_1>commit;
Commit complete.
session2的NLS_LANG设为和客户端操作系统一样(即ZHS16GBK):
C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.ZHS16GBK
连接数据库并插入一条数据:
Session_2>insert into test values(2,'中国');
1 row created.
Session_2>commit;
Commit complete.
5. 执行查询
在session 1上执行查询:
Session_1>select * from test;
        ID VAR
---------- ---------------------
         1 中国
         2 涓   浗
在session 2上执行查询:
Session_2>select * from test;
        ID VAR
---------- --------------------
         1 ???
         2 中国

上面例子看起来很诡异,session1和2都能正常显示自己插入的字符串,又都不能正常显示对方插入的字符串。为了弄清楚,我们首先得知道数据库里对这两个字符串是怎么存储的。我们可以使用dump函数获得字符在数据库的编码:


代码如下:

SQL> select id,dump(var,1016) from test;
ID DUMP(VAR,1016)
-- ------------------------------------------------------------
 1 Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa
 2 Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd

根据AL32UTF8的编码,“中国”两字的正确编码为(都为3个字节):
中--e4,b8,ad
国--e5,9b,bd
因此session 1插入的字符串在数据库中的编码是错误的,session 2正确。这也是为什么一定要设置NLS_LANG为客户端操作系统的字符集。
但是根据上面实验我们可以知道,数据库中存储正确,并不代表客户端能正常显示;同样地,即时数据库没有正确存储,有时候客户端也能够正常显示,这又是为什么呢?别急,请听我慢慢道来:

场景1:session 1插入,session 1查询,在数据库中存储错误,但显示正确。
插入过程:
”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集相同,数据库端对客户端传过来的字符编码不进行任何转换直接存入数据库,因此数据库中存储的编码也是“d6,d0,b9,fa”,
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”d6,d0,b9,fa“在客户端操作系统字符集ZHS16GBK对应的汉字为“中国”。

从以上分析可知,虽然读取时正确的,但那是因为负负得正,实际上数据库中存储是错误的,因此要特别小心这种情况,在生成库中要避免。其实只要对它进行length操作就能轻易揭开它的假面具:


代码如下:

Session_1>select length(var) from test where id=1;
LENGTH(VAR)
-----------
          3

得出的长度居然为3!实际的长度只是2,这会带来很多麻烦。

场景2:session 1插入,session 2查询,在数据库中存储错误,显示也错误。
插入过程和场景1一样,这里就不再累述。
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里编为“d6,d0,b9,fa”无法在客户端操作系统字符集ZHS16GBK里找到对应的编码,所以只好用?代替。

场景3:session 2插入,session 1查询,在数据库中存储正确,但显示错误。
插入过程:
”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集不同,Oracle会进行字符编码转换,也就是将字符集ZHS16GBK里“中国”的编码“d6,d0,b9,fa"转换为字符集"AL32UTF8"里”中国“的编码”e4,b8,ad,e5,9b,bd“。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”e4,b8,ad,e5,9b,bd“在客户端操作系统字符集ZHS16GBK对应的汉字为“涓   浗”(原本2个字符,现在变成了3个字符,因为ZHS16GBK的汉字以2个字节编码)。

场景4:session 2插入,session 2查询,在数据库中存储正确,显示也正确。
插入过程和场景3类似。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里”中国“两字的编码”e4,b8,ad,e5,9b,bd“转换成客户端操作系统字符集ZHS16GBK里“中国”两字的编码“d6,d0,b9,fa",并正常显示。
这种情况虽然经过了两次转换,都确实最正确、最推荐的方式。

附录:Oracle 字符集超集和子集的对应关系可查看:http://download.oracle.com/docs/cd/B19306_01/server.102/b14225/applocaledata.htm#sthref1988
结论:
NLS_LANG只和客户端操作系统的字符集相关,如果客户端操作系统的字符集和数据库字符集间无法正确转换,则应该首先改变客户端终端的字符集,而不是简单地把NLS_LANG设为和数据库字符集一样。

(0)

相关推荐

  • 深入Oracle字符集的查看与修改详解

    一.什么是Oracle字符集 Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系.ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据.它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台. 影响Oracle数据库字符集最重要的参数是NLS_LANG参数.它的格式如下: NLS_LANG = language_territory.charset 它有三个组成部分(语言.地域和字符集),每个成分控制了NLS

  • Oracle字符集修改查看方法

    1.怎么样查看数据库字符集 [A]数据库服务器字符集select * from nls_database_parameters,其来源于props$,是表示数据库的字符集. 客户端字符集环境select * from nls_instance_parameters,其来源于v$parameter, 表示客户端的字符集的设置,可能是参数文件,环境变量或者是注册表 会话字符集环境 select * from nls_session_parameters,其来源于v$nls_parameters,表示

  • oracle查询字符集语句分享

    (1)查看字符集(三条都是等价的) 复制代码 代码如下: select * from v$nls_parameters  where parameter='NLS_CHARACTERSET'select * from  nls_database_parameters where parameter='NLS_CHARACTERSET'select * from props$ where name='NLS_CHARACTERSET' (2)查看国家字符集(三条都是等价的) 复制代码 代码如下:

  • Oracle Faq(Oracle的字符集问题)

    1.在建库时,catproc一定要运行,否则用rman时会出现如下字符集的错误:RMAN-00554: initialization of internal recovery manager package failedRMAN-04005: error from target database: ORA-06553: PLS-213: package STANDARD noeRMAN-04015: error setting target database character set to Z

  • oracle查看字符集和修改字符集使用详解

    一.什么是Oracle字符集Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系.ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据.它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台. 影响Oracle数据库字符集最重要的参数是NLS_LANG参数.它的格式如下: NLS_LANG = language_territory.charset它有三个组成部分(语言.地域和字符集),每个成分控制了NLS子集

  • oracle查看字符集后修改oracle服务端和客户端字符集的步骤

    1.oracle server端字符集查询 复制代码 代码如下: select userenv('language') from dual; server字符集修改: 将数据库启动到RESTRICTED模式下做字符集更改: 复制代码 代码如下: SQL>conn /as sysdba SQL>shutdown immediate; SQL>startup mount SQL>ALTER  SYSTEM  ENABLE  RESTRICTED  SESSION; SQL>ALT

  • Oracle 8i字符集乱码问题析及其解决办法

    正在看的ORACLE教程是:Oracle 8i字符集乱码问题析及其解决办法.一.问题描述 SQL Plus WorkSheet是一个窗口图形界面的SQL语句编辑器,对于那些喜欢窗口界面而不喜欢字符界面的用户,该工具相对SQL/PLUS受到了很大的欢迎.但从Oracle 8i以后,如果安装Oracle 8i时选取的是别于英语的字符集,对于我们中国,通常会选取简体中文字符集(ZHS16GBK),安装成功后,运行SQL Plus WorkSheet程序,会出现所有的中文显示以及查询结果均为乱码的情况.

  • [Oracle] 彻底搞懂Oracle字符集

    基本概念字符集(Character set):是一个系统支持的所有抽象字符的集合.字符是各种文字和符号的总称,包括各国家文字.标点符号.图形符号.数字等.常见的字符集有ASCII,ZHS16GB231280,ZHS16GBK等.字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其它的一个集合(如电脑编码)进行配对.即在符号集合与数字系统之间建立对应关系.与字符集相对应,常见的字符编码有:ASCii,ZHS16GBK,ZH

  • 一文彻底搞懂Java和JDK的版本命名问题

    Java是面向对象的编程语言,在我们开发Java应用的程序员的专业术语里,Java这个单词其实指的是Java开发工具,也就是JDK(Java Development Kit).所以我们常常在CSDN等各大程序员论坛讨论到安装Java8或者JDK8或者JDK1.8或J2SE8或J2SE1.8或J2SE8或J2SE1.8,其实这3个专业词汇的概念是一样的. 告诉庆哥,你对Java的版本号以及JDK的命名真正清楚嘛?比如: Java8 Java SE 8.0 JDK1.8 -- 知道这些是怎么回事嘛?

  • 一文搞懂什么是MySQL前缀索引

    目录 一.什么是前缀索引 二.为什么要用前缀索引 三.怎么创建前缀索引 四.使用前缀索引需要注意的事项 五.小结 一.什么是前缀索引 所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快! 有点类似于 Oracle 中对字段使用 Left 函数来建立函数索引,只不过 MySQL 的这个前缀索引在查询时是内部自动完成匹配的,并不需要使用 Left 函数. 二.为什么要用前缀索引 可能有的同

  • 一文搞懂SQL注入攻击

    目录 1. 前言 2. SQL注入简介 (1)SQL语言 (2)SQL注入 3. SQL注入步骤 (1)发现漏洞 (2)信息收集 (3)攻击Web系统(猜解用户名和密码) (4)获取管理员权限 4. 防范SQL注入 (1)使用参数化查询或存储过程 (2)用户输入检测 (3)SQL语法分析 (4)其他 1. 前言 随着互联网的发展和普及,网络安全问题越来越突出,网络在为用户提供越来越多服务的同时,也要面对各类越来越复杂的恶意攻击.SQL注入(SQL Injection)攻击是其中最普遍的安全隐患之

  • 彻底搞懂Python字符编码

    不论你是有着多年经验的 Python 老司机还是刚入门 Python 不久,你一定遇到过UnicodeEncodeError.UnicodeDecodeError 错误,每当遇到错误我们就拿着 encode.decode 函数翻来覆去的转换,有时试着试着问题就解决了,有时候怎么试都没辙,只有借用 Google 大神帮忙,但似乎很少去关心问题的本质是什么,下次遇到类似的问题重蹈覆辙,那么你有没有想过一次性彻底把 Python 字符编码给搞懂呢? 完全理解字符编码 与 Python 的渊源前,我们有

  • 彻底搞懂 python 中文乱码问题(深入分析)

    前言 曾几何时 Python 中文乱码的问题困扰了我很多很多年,每次出现中文乱码都要去网上搜索答案,虽然解决了当时遇到的问题但下次出现乱码的时候又会懵逼,究其原因还是知其然不知其所以然.现在有的小伙伴为了躲避中文乱码的问题甚至代码中不使用中文,注释和提示都用英文,我曾经也这样干过,但这并不是解决问题,而是逃避问题,今天我们一起彻底解决 Python 中文乱码的问题. 基础知识ASCII 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关

  • 一文搞懂JSON(JavaScript Object Notation)

    目录 JSON出现 Json结构 Json对象 Json对象与JavaScript对象 JSON 和 JavaScript 对象互转 Json数组 复杂数组类型 复杂对象数组组合 对象包含数组 数组包含对象 JSON 语法规则 Json键值对 Json值的数据类型 JSON(JavaScript Object Notation, JS 对象标记)是一种轻量级的数据交换格式,通常用于服务端向网页传递数据 .与 XML 一样,JSON 也是基于纯文本的数据格式. Json 文件的文件后缀是 .Jso

  • oracle的归档模式 ORACLE数据库归档日志常用命令

    --连接恢复管理器 C:\Documents and Settings\mengzhaoliang>rman target/ --归档日志列表 RMAN> list archivelog all; --删除物理文件不存在的归档日志 RMAN> delete expired archivelog all; --删除7天前的归档日志 RMAN> DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7'; oracle的归档模式 一.查看ora

  • 通过示例彻底搞懂js闭包

    例1 function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { console.log(text); } sayAlert(); } sayHello("Bob") // 输出"Hello Bob" 在sayHello()函数中定义并调用了sayAlert()函数:sayAlert()作为内层函数,可以访问外层函数sayHello()中的text变量. 例2 f

随机推荐