Oracle全球支持(即Globalization Support)允许我们使用本地语言和格式来存储和检索数据。通过全球支持,Oracle可以支持多种语言及字符集。
字符集在创建数据库时指定,在创建后通常不能更改,所以在创建数据库时能否选择一个正确的字符集就显得尤为重要.在创建数据库时,我们可以指定字符集(CHARACTER SET)和国家字符集(NATIONAL CHARACTER SET)。
字符集用来存储:
CHAR, VARCHAR2, CLOB, LONG等类型数据
用来标示诸如表名、列名以及PL/SQL变量等
SQL和PL/SQL程序单元等
国家字符集用以存储:
NCHAR, NVARCHAR2, NCLOB等类型数据
Oracle的字符集命名遵循以下命名规则:
<Language><bitsize><encoding>
即: <语言> <比特位数><编码>
比如: ZHS · 16 ·GBK
需要说明的是,有些字符集命名违背了这个规范,Oracle8/Oralce8i中的UTF-8是第一个打破这个命名规范的字符集.我们可以看到一类字符集以 AL开头,如:
AL16UTF16
其中 AL代表 ALL,指适用于所有语言(AllLanguages),按照这个标准当年UTF-8本应被命名为AL24UTF8。
导入导出是我们常用的一个数据迁移及转化工具,因其导出文件具有平台无关性,所以在跨平台迁移中,最为常用。
在导出操作时,非常重要的是客户端的字符集设置,也就是客户端的NLS_LANG设置。
NLS_LANG参数由以下部分组成:
NLS_LANG=<Language>_<Territory>.<Clients Characterset>
NLS_LANG各部分含义如下:
LANGUAGE指定:
-Oracle消息使用的语言
-日期中月份和日显示
TERRITORY指定
-货币和数字格式
-地区和计算星期及日期的习惯
CHARACTERSET:
-控制客户端应用程序使用的字符集
通常设置或者等于客户端(如Windows)代码页
或者对于unicode应用设置为UTF8
在Windows上查看当前系统的代码页可以使用chcp命令:
E:\>chcp
活动的代码页: 936
代码页936也就是中文字符集 GBK,在Microsoft的官方站点上,我们可以遭到关于936代码页的具体编码规则,请参考以下链接:
http://www.microsoft.com/globaldev/reference/dbcs/936.htm
查看客户端NLS_LANG设置可以使用以下方法:
Windows使用:
echo %NLS_LANG%
如:
E:\>echo %NLS_LANG%
AMERICAN_AMERICA.ZHS16GBK
Unix使用:
env|grep NLS_LANG
如:
/opt/oracle>env|grep NLS_LANG
NLS_LANG=AMERICAN_CHINA.ZHS16GBK
Windows客户端设置,可以在注册表中更改NLS_LANG,具体键值位于:
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMExx\
xx指存在多个ORACLE_HOME时系统编号。
导出使用的字符集将会记录在导出文件中,当文件导入时,将会检查导出时使用的字符集设置,如果这个字符集不同于导入客户端的NLS_LANG
设置,字符集将根据导入客户端NLS_LANG设置进行转换,如果必要,在数据插入数据库之前会进行进一步转换。
通常在导出时最好把客户端字符集设置得和数据库端相同,这样可以避免在导出时发生不必要的数据转换,导出文件将和数据库具有相同的字符集。
即使将来会把导出文件导入到不同字符集的数据库中,这样做也可以把转换延缓至导入时刻。
当进行数据导入时,主要存在以下两种情况:
1.源数据库和目标数据库具有相同字符集设置
这时,只需要设置NLS_LANG等于数据库字符集即可导入(前提是,导出使用的是和源数据库相同字符集,即三者相同)
2.源数据库和目标数据库字符集不同
如果我们导出时候使用的NLS_LANG是和源数据库相同的字符集,那么导入时就可以设置客户端NLS_LANG等于导出时使用的字符集,这样转换只发生在数据库端,而且只发生一次。
通常在我们的现实环境中,存在3个字符集设置。
第一:客户端应用字符集(ClientApplication Character Set)
第二:客户端NLS_LANG参数设置
第三:服务器端,数据库字符集(CharacterSet)设置
我们说,一个字符在客户端应用(比如SQLPLUS,CMD,NOTEPAD等)中以怎样的字符显示取决于客户端操作系统,客户端能够显示怎样的字符,我们就可以在应用中录入这些字符,至于这些字符能否在数据库中正常存储,就和另外的两个字符集设置紧密相关了。
在传输过程中,客户端NLS_LANG主要用于进行转换判断
如果NLS_LANG等于数据库字符集,则不进行任何转换直接把字符插入数据库
如果不同则进行转换,转换主要有两个任务
•如果存在对应关系,则把相应二进制编码经过映射后(这一步映射以后,所代表的字符可能发生转换)传递给数据库
•如果不存在对应关系,则传递一个替换字符(很多平台就是?)
数据库字符集,在和客户端NLS_LANG不同时,会把经过NLS_LANG转换的字符进行进一步处理
•对于?(即不存在对应关系的字符)直接以?形式存放入数据库
•对于其他字符,在NLS_LANG和数据库字符集之间进行转换后存入。
以下我们来看一下最为常见的字符集及乱码的产生:
1.当NLS_LANG字符集与数据库字符集不同,同时NLS_LANG不同于Server端字符集设置
在这种情况下,存在两种可能:
•客户端输入的字符在NLS_LANG中没有对应的字符,这时无法转换,NLS_LANG使用替换字符替代这些无法映射的字符(这一步转换在TTS中完成),在很多字符集中这个替代字符就是”?”
•当客户端的字符在NLS_LANG中对应了不同的字符时,传递给数据库以后发生转换,存储的是字符,但是已经丢失了元数据,数据库中的字符不再代表客户端的输入。而且这个过程不可逆,这也就是为什么很多时候在客户端输入的是正常的编码,查询之后会得到未知字符的原因。
3. NLS_LANG和数据库字符集相同时
在这种情况下,数据库端对客户端传递过来的编码不进行任何转换(这样可以提高性能),直接存储进入数据库,那么这时候就存在和上面同样的问题,如果客户端传递过来的字符集在数据库中有正确的对应就可以正确存储,如果没有,就会被替换字符置换成?,乱码就这样产生了
如上图所示,当NLS_LANG和数据库字符集设置相同都为UTF8时,客户端的欧元符号的编码A4就不会经过任何转换就插入到数据库中,而在UTF8的数据库中,A4代表的是一个非法字符。
字符集更改链接
http://www.eygle.com/archives/2004/09/nls_character_set_03.html