在SQL*Plus中用insert插进的都是中文的为什么一存入服务器后再select出的就是???
有的时候服务器数据先导出重装服务器再导入数据结果发生数据查询成???
……
这些问题一般是因为字符集设置不对造成的
很久以来字符集一直是困扰着众多Oracle爱好者的问题笔者从事Oracle数据库管理和应用已经几年了经常接到客户的类似上面提到的有关数据库字符集的告急和求救在此我们就这个问题做一些分析和探讨
首先我们要明确什么是字符集?字符集是一个字节数据的解释的符号集合有大小之分有相互的包括关系如usascii就是zhsgbk的子集从usascii到zhsgbk不会有数据解释上的问题不会有数据丢失Oracle对这种问题也要求从子集到超集的导出受支持反之不行在所有的字符集中utf应该是最大因为它基于unicode双字节保存字符(也因此在存储空间上占用更多)
其次一旦数据库创建后数据库的字符集是不能改变的因此在设计和安装之初考虑使用哪一种字符集是十分重要的数据库字符集应该是操作系统本地字符集的一个超集存取数据库的客户使用的字符集将决定选择哪一个超集即数据库字符集应该是所有客户字符集的超集
在实际应用中和字符集问题关系最大的恐怕就是exp/imp了在做exp/imp时如果Client 和Server的nls_lang设置是一样的一般就没有问题的但是要在两个不同字符集的系统之间导数据就经常会有这样或那样的问题如导出时数据库的显示正常是中文当导入到其他系统时就成了乱码这也是一类常见问题
现在介绍一些与字符集有关的NLS_LANG参数
NLS_LANG格式
NLS_LANG = language_territorycharset
有三个组成部分(语言地域和字符集)每个成分控制了NLS子集的特性其中language 指定服务器消息的语言
territory 指定服务器的日期和数字格式
charset 指定字符集
例如
AMERICAN_AMERICAUSSCII
AMERICAN _ AMERICA ZHSGBK
还有一些子集可以更明确定义NLS_LANG参数
DICTBASE 数据字典基本 表版本
DBTIMEZONE 数据库时区
NLS_LANGUAGE 语言
NLS_TERRITORY 地域
NLS_CURRENCY 本地货币字符
NLS_ISO_CURRENCY ISO货币字符
NLS_NUMERIC_CHARACTERS 小数字符和组 分隔开
NLS_CHARACTERSET 字符集
NLS_CALENDAR 日历系统
NLS_DATE_FORMAT 缺省的日期格式
NLS_DATE_LANGUAGE 缺省的日期语言
NLS_SORT 字符排序序列
NLS_TIME_FORMAT 时间格式
NLS_TIMESTAMP_FORMAT 时间戳格式
……
通过props$动态性能视图我们可以查看数据库的字符集信息
$> sqlplus internal
SQL> desc props$
Name Type Nullable Default Comments
NAME VARCHAR()
VALUE$ VARCHAR() Y
COMMENT$ VARCHAR() Y
SQL> set arraysize
SQL> col value$ format a
SQL> select namevalue$ from props$ where name=NLS_CHARACTERSET;
NAME VALUE$
NLS_CHARACTERSET ZHSGBK
SQL> select * from sysprops$;
NAME VALUE$
DICTBASE
DBTIMEZONE :
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS
NLS_CHARACTERSET ZHSGBK
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DDMONRR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HHMI SSXFF AM
NLS_TIMESTAMP_FORMAT DDMONRR HHMISSXFF AM
NLS_TIME_TZ_FORMAT HHMI
SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT DDMON RR HHMI SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_NCHAR_CHARACTERSET ZHSGBK
NLS_RDBMS_VERSION
NAME VALUE$
GLOBAL_DB_NAME SCPDB
EXPORT_VIEWS_VERSION
rows selected
SQL>
从结果可以看出
NLS_LANG = AMERICAN _ AMERICA ZHSGBK
虽然数据库的字符集是在create database的时候指定的以后不允许改变但在一个已经建立好的数据库上我们可以通过修改SYSPROPS$来修改主要是对应客户端的显示与存储无关
如
SQL> conn / as sysdba
Connected
SQL> SQL> select * from sysprops$
WHERE NAME=NLS_LANGUAGE;
NAME VALUE$
NLS_LANGUAGE AMERICAN
SQL>
SQL> UPDATE sysPROPS$ SET VALUE$=SIMPLIFIED CHINESE
WHERE NAME=NLS_LANGUAGE;
row updated
SQL>
SQL> select * from sysprops$
WHERE NAME=NLS_LANGUAGE;
NAME VALUE$
NLS_LANGUAGE SIMPLIFIED CHINESE
SQL>
通常出现问题的原因可分为三种
服务器指定字符集与客户字符集不同而与加载数据字符集一致
解决方法对于这种情况只需要设置客户端字符集与服务器端字符集一致就可以了具体操作如下
* 查看当前字符集
SQL> select * from sysprops$
WHERE NAME=NLS_CHARACTERSET;
NAME VALUE$
NLS_CHARACTERSET ZHSGBK
SQL>
可以看出现在服务器端Oracle数据库的字符集为ZHSGBK
* 根据服务器的字符集在客户端作相应的配置或者安装Oracle的客户端软件时指定
如果还没安装客户端那么在安装客户端时指定与服务器相吻合的字符集即可如果已经安装好了客户端并且客户端为 sql*net 以下版本进入Windows的系统目录编辑oracleini文件用USASCII替换原字符集重新启动计算机设置生效否则如果客户端为 sql*net 以上版本在Win 下 运 行REGEDIT第一步选HKEY_LOCAL_MACHINE第二步选择SOFTWARE 第三步选择 Oracle 第四步选择 NLS_LANG 键 入 与服 务 器 端 相 同 的 字 符 集
(本例为HKEY_LOCAL_MACHINE\
SOFTWARE\ORACLE\NLS_LANG AMERICAN _ AMERICA ZHSGBK)
如果是UNIX客户端则
SQL> conn / as sysdba
Connected
SQL> SQL> UPDATE sysPROPS$ SET VALUE$=SIMPLIFIED CHINESE
WHERE NAME=NLS_LANGUAGE;
row updated
SQL> COMMIT;
Commit complete
SQL>
服务器指定字符集与客户字符集相同与加载数据字符集不一致
解决方法强制加载数据字符集与服务器端字符集一致要做到这一点可以通过重新创建数据库并选择与原卸出数据一致的字符集然后IMP数据这种情况仅仅适用于空库和具有同一种字符集的数据
解决这类问题也可以先将数据加载到具有相同字符集的服务器上然后用转换工具卸出为foxbase 格式或access格式数据库再用转换工具转入到不同字符集的Oracle数据库中这样就避免了Oracle字符集的困扰目前数据库格式转换的工具很多像power builder以上版本提供的pipeline及Microsoft Access数据库提供的数据导入/导出功能等
服务器指定字符集与客户字符集不同与输入数据字符集不一致
对于这种情况目前为止都还没有太好的解决方法
通过上面的了解我们知道导致在后期使用数据库时出现种种关于字符集的问题多半是由于在数据库设计安装之初没有很好地考虑到以后的需要所以我们完全可以通过在服务器上和客户端使用相同的字符集来避免由此类问题引出的麻烦