1、字符集
字符集实质上是将字符表示为数值编码的一组比特序列。
Oracle中常用的字符集包括US7ASCII、WE8ISO8859P1、ZHS16GBK、AL32UTF8等。遵循以下命名规则:
<Language><bit size><enCoding>
<语 言><比特数><编码>
ZHS 16 GBK
如果只需要存储英文信息,那么选择US7ASCII作为字符集就可以;但是如果要存储中文,那么我们就需要选择能够支持中文的字符集(如ZHS16GBK);如果需要存储多国语言文字,那就要选择UTF8了,但是需要额外的存储空间和网络传输。
当一种字符集(字符集A)的编码数值包含另一种字符集(字符集B)的所有编码数值,并且两种字符集相同编码数值代表相同的字符时,则字符集A是字符集B的超级,或称字符集B是字符集A的子集。由于US7ASCII是最早的Oracle数据库编码格式,因此有许多字符集是US7ASCII的超集,例如WE8ISO8859P1、ZHS16GBK、AL32UTF8。
Oracle的字符集设置包含3个部分:服务器端字符集、客户端字符集以及客户端应用字符集。
1.1、服务器端字符集
在创建数据库时,可以指定字符集(CHaraCTER SET)和国家字符集(NATIONAL CHaraCTER SET)。
字符集用来存储CHAR、VARCHAR2、CLOB、LONG等类型数据;标示诸如表名、列名以及 PL/sql变量等; sql和PL/sql程序单元等。
国家字符集用以存储NCHAR、NVARCHAR2、NCLOB等类型数据。
使用DBCA创建数据库时,在以下界面选择字符集和国家字符集。
数据库字符集实际上说明这个数据库所能处理的字符的集合及其编码方式,由于字符集选定后再进行更改会有诸多的限制,所以在数据库创建时一定要考虑清楚后再选择。
可以查询以下数据字典或视图查看字符集设置情况:
nls_database_parameters、sys.props$、v$nls_parameters
查询结果中NLS_CHaraCTERSET表示字符集,NLS_NCHAR_CHaraCTERSET表示国家字符集。
还有下面一种比较直观的查询方法:
sql> select USERENV ('language') from dual;
USERENV ('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.US7ASCII
1.2、客户端字符集
客户端字符集定义了客户端字符数据的编码方式,任何发自或发往客户端的字符数据均使用客户端定义的字符集编码。客户端字符集是通过设置NLS_LANG参数来设定的。
NLS_LANG=<language>_<territory>.<clIEnt character set>
其中,Language是语言,显示Oracle消息、校验、日期命名;Territory是地区,指定默认日期、数字、货币等格式;ClIEnt character set:指定客户端将使用的字符集。
NLS_LANG中真正影响字符集设置的是第三部分,如果设置与服务器字符集不同,在传输过程中需要进行字符集转换。转换是Oracle自动进行的,但是可能会产生数据丢失。
客户端字符集的设置方法如下:
windows:HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_OraDb10g_home1下的NLS_LANG项;或者在CMD中设置环境变量(优先级高,但只作用于当前窗口):
C:\ > set NLS_LANG=SIMPliFIED CHInesE_CHINA.ZHS16GBK
Unix:设置环境变量NLS_LANG
$NLS_LANG=“simplifIEd chinese”_china.zhs16gbk
$export NLS_LANG
1.3、客户端应用字符集
客户端应用,如sqlPLUS、CMD、NOTEPAD等能够显示什么样的字符取决于客户端 *** 作系统语言环境。客户端能够显示什么字符,就可以在应用中录入这些字符。字符能否在数据库中正常存储,只与另外的两个字符集设置相关。
2、一些测试
测试环境:
服务器端windows Server2003,Oracle10G;
客户端windows XP,简体中文。在CMD中右键“属性”查看代码页:
2.1、测试一
条件:客户端字符集与数据库字符集相同,与客户端应用字符集不同。
设置客户端字符集:
C:\documents and Settings\dongxuyang>set NLS_LANG=AMERICAN_AMERICA.US7ASCII
查看数据库服务器端字符集:
sql> conn scott/tiger@us7
Connected.
sql> select * from nls_database_parameters where parameter='NLS_CHaraCTERSET';
ParaMETER VALUE
------------------------------ ----------------------------------------
NLS_CHaraCTERSET US7ASCII
创建测试表:
sql> create table test(v1 varchar2(10));
table created.
sql> insert into test values('中国');
1 row created.
sql> select * from test;
V1
----------
中国
数据的存取与显示都正确,好像没有什么问题,但是US7ASCII字符集只定义了128个符号,并不支持汉字。查看一下实际存储的内容:
sql> select dump(v1,1016) from test;
DUMP(V1,1016)
--------------------------------------------------------------------------------
Typ=1 Len=4 CharacterSet=US7ASCII: d6,d0,b9,fa
由于Oracle检查数据库与客户端的字符集设置是同样的(US7ASCII),那么数据在客户端与数据库之间的存取过程中将不发生任何转换。使用Oracle10G提供的Locale Builder工具查看“中国”在输入系统中(ZHS16GBK)的编码如下:
显然,Oracle直接将“中国”的ZHS16GBK内码(0xd6d0和0xb9fa)存入了US7ASCII字符集中。
同样在select查询语句中,Oracle将数据库中的编码直接传输到客户端而不进行转换,客户端 *** 作系统(ZHS16GBK)正好能识别为“中国”。
这种使用方式表面上好像没有问题,实际上却可能存在隐患。例如,在应用 length或substr等字符串函数时,就可能得到意外的结果。
sql> select length(v1) from test;
LENGTH (V1)
------------------
4
如果使用ZHS16GBK存储,结果为2。
2.2、测试二
条件:客户端字符集与客户端应用字符集相同,与数据库字符集不同。
设置客户端字符集为ZHS16GBK:
C:\documents and Settings\dongxuyang>set NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
查看之前录入的数据:
sql> conn scott/tiger@us7
Connected.
sql> select * from test;
V1
--------------------
VP9z
由于Oracle检查发现数据库设置的字符集与客户端配置字符集不同,它将对数据进行字符集的转换。
数据库中实际存放的数据为 0xD6(11010110)、0xD0(11010000)、0xB9(10111001)、0xFA(11111010)。US7ASCII是一个7bit的字符集,存储在8bit的字节中, Oracle忽略各字节的最高bit,则D6(11010110)就变成了 0x56(01010110),在ZHS16GBK中代表数字符号“V”(当然在其它字符集中也是“V”)。
其它3个字节也一样进行转换,这样“中国”就变成了“VP9z”。
此时,再插入一个记录“中国”:
sql> insert into test values('中国');
1 row created.
sql> select * from test;
V1
--------------------
VP9z
??
同样,Oracle检查发现数据库设置的字符集与客户端不一致,插入数据时需要进行转换。但字符集ZHS16GBK中的“中国”两字在US7ASCII中没有对应的字符,所以Oracle用统一的“替换字符”插入数据库,即“?”,编码为0x3F(00111111)。
sql> select dump(v1,fa
Typ=1 Len=2 CharacterSet=US7ASCII: 3f,3f
此时,输入的信息实际上已经丢失。不管字符集设置如何改变,第二行查询的结果都是两个“?”(注意是2个,而不是4个)。
如果再次更改客户端字符集为US7ASCII:
C:\documents and Settings\dongxuyang>set NLS_LANG=AMERICAN_AMERICA.US7ASCII
sql> conn scott/tiger@us7
Connected.
sql> select * from test;
V1
----------
中国
??
可以显示用US7ASCII插入的“中国”,而用ZHS16GBK插入的“中国”实际上已存储为两个“?”,编码为0x3F。
2.3、测试三
条件:更改服务器字符集为ZHS16GBK
数据库创建后通常不建议更改字符集,在此只做测试使用。更改前进行全库备份。
sql> connect sys/password@us7 as sysdba;
Connected.
sql> update props$ set value$='ZHS16GBK' where name='NLS_CHaraCTERSET';
1 row updated.
sql> commit;
Commit complete.
sql> select * from nls_database_parameters where parameter='NLS_CHaraCTERSET';
ParaMETER
------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
NLS_CHaraCTERSET
ZHS16GBK
关闭数据库,重新启动。
客户端字符集与数据库字符集以及客户端应用字符集三者相同
设置客户端字符集为ZHS16GBK:
C:\documents and Settings\dongxuyang> SET NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
sql> conn scott/tiger@us7
Connected.
sql> select * from test;
V1
--------------------
中国
??
sql> select dump(v1,3f
结果与“测试一”相同,这是因为客户端字符集与数据库字符集相同,不需要转换;同时客户端应用可以显示中文。
此时再插入一行数据:
sql> insert into test values('中国');
1 row created.
sql> commit;
Commit complete.
sql> select * from test;
V1
--------------------
中国
??
中国
sql> select dump(v1,3f
Typ=1 Len=4 CharacterSet=US7ASCII: d6,fa
同样可以正确显示新插入的一行。
数据库字符集与客户端应用字符集相同,与客户端字符集不同
更改客户端字符集为US7ASCII:
C:\documents and Settings\dongxuyang>set NLS_LANG=AMERICAN_AMERICA.US7ASCII
sql> conn scott/tiger@us7
Connected.
sql> select * from test;
V1
----------
??
??
??
由于Oracle检查发现数据库字符集为ZHS16GBK而客户端字符集为US7ASCII,需要进行字符集转换。
第一行与第三行的汉字“中”与“国”在客户端字符集US7ASCII中没有对应字符,所以转换为“替换字符”。第二行数据在数据库中存的就是两个“?”号。在客户端显示的三行都是两个“?”号,但在数据库中存储的内容却是不同的。
再次插入一行数据:
sql> insert into test values('中国');
1 row created.
sql> commit;
Commit complete.
sql> select * from test;
V1
----------
??
??
??
VP9z
sql> select dump(v1,fa
Typ=1 Len=2 CharacterSet=US7ASCII: 3f,fa
Typ=1 Len=4 CharacterSet=US7ASCII: 56,50,39,7a
在客户端字符集设置为US7ASCII时,向字符集为ZHS16GBK的数据库中插入“中国”,需要进行字符转换。“中国”的ZHS16GBK编码为D6(11010110)、D0(11010000)和B9(10111001)、FA(11111010)。由于US7ASCII为7bit编码,Oracle将这两个汉字当作四个字符并忽略各字节的最高位,从而存入数据库的编码就变成了0x56(01010110)、0x50(01010000)与0x39(00111001)、0x7A(01111010),也就是“VP9z”,原始信息被改变了。
此时,即使再将客户端字符集改为与数据库字符集相同,最新插入的数据(第四个“中国”)也不能再还原了。
C:\documents and Settings\dongxuyang>set NLS_LANG=AMERICAN_AMERICA. ZHS16GBK
QL> conn scott/tiger@us7
Connected.
sql> select * from test;
V1
--------------------
中国
??
中国
VP9z
为了在使用Oracle时少一些字符集的问题,建议使用以下设置:
l 创建数据库时选择当前与将来都能满足需求的字符集;
l 客户端设置为 *** 作系统默认的字符集
但即使按照以上设置,如果客户端字符集与数据库字符集不相同,就会产生转换;因而可能产生信息丢失。如数据库字符集为AL32UTF8,客户端字符集为ZHS16GBK时,使用PL/sql Developer 8登陆时会提示以下警告信息:
3、更改数据库字符集
数据库字符集在创建后原则上不能更改。如果需要修改字符集,通常需要导出数据库数据,重建数据库,再导入数据库数据的方式来转换。
也可以通过ALTER DATABASE CHaraCTER SET语句修改字符集,但只有新的字符集是当前字符集的超集时才能修改数据库字符集。
sql> select userenv('language') from dual;
USERENV('LANGUAGE')
----------------------------------------------------
SIMPliFIED CHInesE_CHINA.ZHS16GBK
sql> alter database character set ZHS16CGB231280;
alter database character set ZHS16CGB231280
*
ERROR 位于第 1 行:
ORA-12712: 新字符集必须为旧字符集的超集
上文“测试三”中使用了修改props$的方法修改数据库字符集,实际上该方法存在许多隐患。以下测试修改数据库字符集的过程,以SYS登录:
sql> select * from props$ where name='NLS_CHaraCTERSET';
name
------------------------------------------------------------
VALUE$
--------------------------------------------------------------------------------
COMMENT$
--------------------------------------------------------------------------------
NLS_CHaraCTERSET
US7ASCII
Character set
sql> show user
USER is "SYS"
sql> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
sql> startup nomount
ORACLE instance started.
Total System Global Area 180355072 bytes
Fixed Size 788028 bytes
Variable Size 145750468 bytes
Database Buffers 33554432 bytes
Redo Buffers 262144 bytes
sql> alter database mount;
Database altered.
打开跟踪功能:
sql> alter session set sql_trace=true;
Session altered.
sql> alter system enable restricted session;
System altered.
sql> alter system set job_queue_processes=0;
System altered.
sql> alter system set aq_tm_processes=0;
System altered.
sql> alter database open;
Database altered.
sql> alter database character set ZHS16GBK;
alter database character set ZHS16GBK
*
ERROR at line 1:
ORA-12716: Cannot ALTER DATABASE CHaraCTER SET when CLOB data exists
数据库中存在CLOB字段时不允许修改字符集,可以先删除这些表,修改字符集后再重建。
sql> truncate table MetaSTYLESHEET;
table truncated.
同样删除以下表:
sql> truncate table RulE$;
table truncated.
sql> truncate table WRH$_sqlTEXT;
table truncated.
sql> truncate table WRI$_DBU_FEATURE_USAGE;
table truncated.
sql> truncate table WRI$_DBU_FEATURE_MetaDATA;
table truncated.
sql> truncate table WRI$_DBU_HWM_MetaDATA;
table truncated.
sql> truncate table DMSYS.DM$PMML_DTD;
table truncated.
sql> truncate table mdsYS.SDO_GEOR_XMLSCHEMA_table;
table truncated.
sql> truncate table SYSMAN.MGMT_JOB_OUTPUT;
table truncated.
修改数据库字符集:
sql> alter database character set ZHS16GBK;
Database altered.
sql> ALTER SESSION SET sql_TRACE=False;
Session altered.
sql> disconn
disconnected from Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning,olAP and Data Mining options
sql> exit
在初始化参数user_dump_dest指定的目录下找到最新的跟踪文件,可以看到以上过程主要涉及了12个基本表。因此,“测试三”中的方法只是进行了很小一部分的 *** 作。
update col$ set charsetID = :1 where charsetform = :2
update argument$ set charsetID = :1 where charsetform = :2
update collection$ set charsetID = :1 where charsetform = :2
update attribute$ set charsetID = :1 where charsetform = :2
update parameter$ set charsetID = :1 where charsetform = :2
update result$ set charsetID = :1 where charsetform = :2
update partcol$ set spare1 = :1 where charsetform = :2
update subpartcol$ set spare1 = :1 where charsetform = :2
update props$ set value$ = :1 where name = :2
update "SYS"."KOTAD$" set SYS_NC_ROWINFO$ = :1 where SYS_NC_OID$ = :2
update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6,cache=:7,highwater=:8,audit$=:9,flags=:10 where obj#=:1
update kopm$ set Metadata = :1, length = :2 where name='DB_FDO'
4、导入与导出
import / Export是Oracle数据库导入与导出的一对工具。
4.1导出过程
在Export过程中,如果数据库字符集与Export用户会话字符集(NLS_LANG)不一致,则会发生字符集转换,并在导出文件的头部几个字节中存储Export用户会话字符集的ID号。在这个转换过程中可能发生数据的丢失。
例如:源数据库使用ZHS16GBK,而Export用户会话字符集使用US7ASCII。在这个转换过程中,中文字符在US7ASCII中不能够找到对等的字符,所以所有中文字符都会丢失而变成“?? ”形式,这样转换后生成的导出文件已经发生了数据丢失。
因此如果想正确导出源数据库数据,则Export过程中用户会话字符集应等于源数据库字符集或是源数据库字符集的超集。
4.2导入过程
查看导出文件的字符集设置:
导出文件的第2和第3个字节记录了文件的字符集。如果文件不大,可以用ultraEdit打开(16进制方式),然后用以下sql查出它对应的字符集:
sql> select nls_charset_name(to_number('0354','xxxx')) from dual;
ZHS16GBK
确定导入session的字符集,即导入Session使用的NLS_LANG环境变量。IMP进程读取导出文件字符集ID,和导入进程的NLS_LANG进行比较。如果导出文件字符集和导入Session字符集相同,那么在这一步骤内就不需要转换;否则,就需要把数据转换为导入Session使用的字符集。
可以看出,导入数据到数据库过程中发生两次字符集转换:导入文件字符集与导入Session使用的字符集之间的转换;导入Session字符集与数据库字符集之间的转换。
当数据库字符集不等于EXP过程中NLS_LANG参数,且数据库字符集是EXP过程中NLS_LANG的子集才能保证导出文件正确,其他情况则导出文件字符乱码。
导出文件字符集(EXP过程中NLS_LANG)不等于IMP过程中NLS_LANG字符集,且导出文件字符集是IMP过程中NLS_LANG的子集才能保证第一次转换正常;否则,出现乱码。
如果第一次转换正常,IMP过程中NLS_LANG字符集是目标数据库字符集的子集或相同才能保证第二次转换正常,否则第二次转换中出现乱码。
Done.
总结以上是内存溢出为你收集整理的Oracle 数据库字符集总结全部内容,希望文章能够帮你解决Oracle 数据库字符集总结所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)