字符集总结
author:skate
time:2007-12-4
最近公司的数据库要迁移,所以就此机会总结下字符集的知识,以便自己对字符集更全面,更深入的认识。用了
一小天的时间,我是边测试边写,
1.
什么是oracle字符集
Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系。ORACLE 支持国家语言的体系结构,允许你使用本地化语言来存储,处理,检索数据。它是数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台。
NLS_LANG
这个参数很重要,直接影响数据库字符集,它的格式如下:
NLS_LANG = language_territory.charset
它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。其中:
Language 指定服务器消息的语言
territory 指定服务器的日期和数字格式
charset 指定字符集。如:AMERICAN _ AMERICA. ZHS16GBK
从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文
2.
查询oracle数据库的字符集
在我们做数据导入的时候,因为字符集的原因不能正确导入,主要涉及三方面的原因,
一是 oracle server的字符集
二是 oracle client的字符集
三是 dmp字符集
如果要正确导入数据,要保证这三个字符集相同
1)查询oracle server端字符集
sql> select userenv('language') language from dual;
LANGUAGE
----------------------------------------------------
SIMPLIFIED CHINESE_CHINA.ZHS32GB18030
1 rows selected
2)查询dmp的字符集
用oracle的exp导出的dmp字符集包含了字符集的信息,dmp的第2,3个字符就是dmp的字符集信息,如果dmp文件比较小,我们可以用UltraEdit(16进制)工具打开,例如第2,3字节是0354,那么我们可以用sql查出对应的字符集
sql> select nls_charset_name(to_number(0354,'xxxx')) charset from dual
CHARSET
----------------------------------------
ZHS16GBK
1 rows selected
相反如果我知道字符集,我怎么能知道它对应的16进制数呢?我们还是通过sql来查到
sql> select to_char(nls_charset_id('ZHS16GBK'),'xxxx') id from dual
ID
-----
354
1 rows selected
这样得到0354,然后用0354替换dmp的第2,3个字节,这样就达到改变dmp的字符集,进而欺骗oracle,以完成数据的导入
但是如果文件很大,再用UltraEdit就打不开了,可以用linux命令:
cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6
3
字符集的转换
严格来讲,数据库的字符集是在创建数据库时选择的,一旦数据库创建好,就不能在更改字符集了,除非重新安装数据库,但是也不是一定要重装数据库,可以有变通的办法完成字符集的转换
单字节=>单字节
多字节=>多字节
且是子集到超集时,可以用alter database CHARACTER set newcharset 更新字符集;
但是,通常对于单字节向多字节转换和非子集到超集转换时采用exp/imp+internal_use的方法
SQL> select * from v$nls_parameters;
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_CHARACTERSET WE8ISO8859P1
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI
NLS_DUAL_CURRENCY $
NLS_NCHAR_CHARACTERSET UTF8
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
19 rows selected.
SQL>
将数据库启动到RESTRICTED模式下做字符集更改:
SQL> conn /as sysdba
Connected.
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup mount
ORACLE instance started.
Total System Global Area 236000356 bytes
Fixed Size 451684 bytes
Variable Size 201326592 bytes
Database Buffers 33554432 bytes
Redo Buffers 667648 bytes
Database mounted.
SQL> ALTER SESSION SET SQL_TRACE=TRUE;//语句跟踪
System 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-12712: new character set must be a superset of old character set
提示我们的字符集:新字符集必须为旧字符集的超集,这时我们可以跳过超集的检查做更改:
SQL> ALTER DATABASE character set INTERNAL_USE ZHS16GBK;
Database altered.
SQL> select * from v$nls_parameters;
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_CHARACTERSET ZHS16GBK
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI
NLS_DUAL_CURRENCY $
NLS_NCHAR_CHARACTERSET UTF8
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
19 rows selected.
重启检查是否更改完成:
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 236000356 bytes
Fixed Size 451684 bytes
Variable Size 201326592 bytes
Database Buffers 33554432 bytes
Redo Buffers 667648 bytes
Database mounted.
Database opened.
SQL> select * from v$nls_parameters;
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_CHARACTERSET ZHS16GBK
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
PARAMETER VALUE
---------------------------------------------------------------- ---------------
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI
NLS_DUAL_CURRENCY $
NLS_NCHAR_CHARACTERSET UTF8
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
19 rows selected.
在Oracle9i中,如果数据库存在CLOB类型字段,那么就不允许对字符集进行转换
对于不同情况,Oracle提供不同的解决方案,如果是用户数据表,一般我们可以把包含CLOB字段的表导出,然后drop掉相关对象,
转换后再导入数据库;对于系统表,可以按照以下方式处理:
SQL> truncate table Metastylesheet;
Table truncated.