经常碰到SQLPLUS展现乱码的问题,字符集和相关的定义都有说明但是很少有能把这些关系说的很简单易懂的。
在此之前我们需要搞清楚三个概念,操作系统字符集,客户端字符集,Oracle字符集:
操作系统字符集:对应的参数是LANG,这个参数应该是Oracle数据库的超集,如果操作系统不支持,那么我们的数据就会乱码。这里的操作系统指的是客户端的操作系统。服务器端的操作系统不会影响数据的存取。
数据库字符集:NLS_CHARACTERSET,可以在nls_database_parameters中查看当前数据库的字符集,安装数据库的时候选择,一般不修改,不过在新的字符集是现有字符集的严格超集的情况下可以改,其他情况下修改可能导致数据库异常。例如将UTF8字符集修改为AL32UTF8
关于子集超集的映射关系,见如下Oracle官网的文档的Binary Subset-Superset Pairs。
http://docs.oracle.com/database/121/NLSPG/applocaledata.htm#NLSPG591
客户端字符集:对应的参数是NLS_LANG,如果客户端未设置,此时则取的是安装时数据库的默认参数
为了帮助理解,我画了一张图如下,图中标红部分如果一致表示数据的存储方式一致,即如果LANG、NLS_LANG、NLS_CHARACTERSET的编码是一致的如UTF8,那么数据的传输过程中不会异常,字符乱码只是显示问题。
1、操作系统字符集
linux下首先locale 查看字符集
- [oracle@oddpc ~]$ locale
- LANG=en_US.UTF-8
- LC_CTYPE="en_US.UTF-8"
- LC_NUMERIC="en_US.UTF-8"
- LC_TIME="en_US.UTF-8"
- LC_COLLATE="en_US.UTF-8"
- LC_MONETARY="en_US.UTF-8"
- LC_MESSAGES="en_US.UTF-8"
- LC_PAPER="en_US.UTF-8"
- LC_NAME="en_US.UTF-8"
- LC_ADDRESS="en_US.UTF-8"
- LC_TELEPHONE="en_US.UTF-8"
- LC_MEASUREMENT="en_US.UTF-8"
- LC_IDENTIFICATION="en_US.UTF-8"
- LC_ALL=
- [oracle@oddpc ~]$ echo $LANG
- en_US.UTF-8
2、该主机并未安装中文支持包,设置LANG后可以效果如下,显然无路如何调整NLS_LANG在这台机器上都无法展现中文
- [oracle@evenpc ~]$ export LANG=zh_CN.utf8
- [oracle@evenpc ~]$ date
- 2016? 10? 13? ??? 15:17:01 CST
3、安装中文支持包,使用yum -y groupinstall chinese-support 可以安装中文支持包,安装过程略过,安装完毕后可以正常显示中文
- [oracle@oddpc ~]$ export LANG=zh_CN.utf8
- [oracle@oddpc ~]$ date
- 2016年 10月 13日 星期四 15:14:19 CST
4、接下来就是展现测试,我安装了两个数据库实例PROD1和PROD5,PROD1 的字符集是WE8MSWIN1252,PROD5的字符集是AL32UTF8
默认情况下NLS_LANG是空的,此时NLS_LANG取默认安装时的值,PROD1是AMRICAN,PROD5是SIMPLIFIED CHINESE
- [oracle@oddpc ~]$ echo $NLS_LANG
- [oracle@oddpc ~]$
- SQL> show parameter lang
-
- NAME TYPE VALUE
-
- nls_date_language string
- nls_language string AMERICAN
- SQL> select sysdate from dual;
-
- SYSDATE
-
- 13-OCT-16
PROD5
- SQL> show parameter lang
-
- NAME TYPE VALUE
-
- nls_date_language string
- nls_language string SIMPLIFIED CHINESE
- SQL> select sysdate from dual;
-
- SYSDATE
-
- 13-10?-16
5、PROD5 发生乱码,PROD1英文正常,设置下NLS_LANG参数
PROD1 的结果如下,可以看到提示信息已经变成中文,但是由于字符集非UTF8中文字符存入后将乱码
- [oracle@oddpc ~]$ export NLS_LANG="SIMPLIFIED CHINESE_CHINA.UTF8"
- [oracle@oddpc ~]$ sqlplus / as sysdba
-
- SQL*Plus: Release 11.2.0.3.0 Production on 星期四 10月 13 15:42:46 2016
-
- Copyright (c) 1982, 2011, Oracle. All rights reserved.
-
-
- 连接到:
- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
- With the Partitioning, OLAP, Data Mining and Real Application Testing options<pre name="code" class="sql">SQL> show parameter lang </pre <>
-
- NAME TYPE VALUE
-
- nls_date_language string SIMPLIFIED CHINESE
- nls_language string SIMPLIFIED CHINESE
- SQL> show parameter db_name
-
- NAME TYPE VALUE
-
- db_name string PROD1
- SQL> show parameter lang
-
- NAME TYPE VALUE
-
- nls_date_language string SIMPLIFIED CHINESE
- nls_language string SIMPLIFIED CHINESE
- SQL> select sysdate from dual;
-
- SYSDATE
-
- 13-10? -16
- SQL> select * from nls_database_parameters;
-
- PARAMETER VALUE
-
- NLS_LANGUAGE AMERICAN
- NLS_TERRITORY AMERICA
- NLS_CURRENCY $
- NLS_ISO_CURRENCY AMERICA
- NLS_NUMERIC_CHARACTERS .,
- NLS_CHARACTERSET WE8MSWIN1252
- NLS_CALENDAR GREGORIAN
- NLS_DATE_FORMAT DD-MON-RR
- NLS_DATE_LANGUAGE AMERICAN
- NLS_SORT BINARY
- NLS_TIME_FORMAT HH.MI.SSXFF AM
-
- PARAMETER VALUE
-
- NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
- NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
- NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
- NLS_DUAL_CURRENCY $
- NLS_COMP BINARY
- NLS_LENGTH_SEMANTICS BYTE
- NLS_NCHAR_CONV_EXCP FALSE
- NLS_NCHAR_CHARACTERSET AL16UTF16
- NLS_RDBMS_VERSION 11.2.0.3.0
-
- 已选择20行。
PROD5的结果如下,此时PROD5显示正常
- [oracle@oddpc ~]$ export NLS_LANG="SIMPLIFIED CHINESE_CHINA.UTF8"
- [oracle@oddpc ~]$ sqlplus / as sysdba
- SQL*Plus: Release 11.2.0.3.0 Production on 星期四 10月 13 15:46:36 2016
-
- Copyright (c) 1982, 2011, Oracle. All rights reserved.
-
-
- 连接到:
- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
- With the Partitioning, OLAP, Data Mining and Real Application Testing options
- SQL> show parameter db_name
-
- NAME TYPE VALUE
-
- db_name string PROD5
- SQL> select sysdate from dual;
-
- SYSDATE
-
- 13-10月-16
-
- SQL> show parameter lang
-
- NAME TYPE VALUE
-
- nls_date_language string SIMPLIFIED CHINESE
- nls_language string SIMPLIFIED CHINESE<pre name</pre ="code" class="sql">SQL> select * from nls_database_parameters;
-
- PARAMETER VALUE
-
- NLS_LANGUAGE AMERICAN
- NLS_TERRITORY AMERICA
- NLS_CURRENCY $
- NLS_ISO_CURRENCY AMERICA
- NLS_NUMERIC_CHARACTERS .,
- NLS_CHARACTERSET AL32UTF8
- NLS_CALENDAR GREGORIAN
- NLS_DATE_FORMAT DD-MON-RR
- NLS_DATE_LANGUAGE AMERICAN
- NLS_SORT BINARY
- NLS_TIME_FORMAT HH.MI.SSXFF AM
-
- PARAMETER VALUE
-
- NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
- NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
- NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
- NLS_DUAL_CURRENCY $
- NLS_COMP BINARY
- NLS_LENGTH_SEMANTICS BYTE
- NLS_NCHAR_CONV_EXCP FALSE
- NLS_NCHAR_CHARACTERSET AL16UTF16
- NLS_RDBMS_VERSION 11.2.0.3.0
-
- 已选择20行。
总结:通过以上的实验可以看出,客户端展现是否乱码是由NLS_LANG决定,发生中文乱码的情况下,首先查看数据库的NLS_CHARACTERSET是否支持中文存储,如果不支持,无论如何设置均无法正常显示中文。Oracle官方文档上给出了各种语言的编码支持如下。
字符集问题的初步探讨(一)-字符集的基本知识
Oracle全球支持(即Globalization Support)允许我们使用本地语言和格式来存储和检索数据。通过全球支持,Oracle可以支持多种语言及字符集,得以展示数据库的强大魅力。
由于不同语言及字符集的共同存储存在设置上具有一定的复杂性,字符集一度成为普遍困扰大家的一个主要问题。
本文试图就一些常见问题进行探讨,希望可以把一些实际经验共享给大家!
1. 字符集的基本知识
如果从头说起,字符集最早的编码方案来自于与ASCII.
这也是我们最常见的编码方式。该方案起源于1960年代初期,最初是美国国会图书馆制定用来作为美国图书馆界书目交换的共同标准,最后完善成为美国的国家标准ASCII(American Standard Codefor Information Interchange),之后进一步演变成世界性的计算机字符编码标准ISO646(其全名为7-bit coded character set for information interchange)。成为计算机编码方案的基础。
Oracle数据库最早支持的编码方案也就是US7ASCII.
但是我们知道,英文字符一般是以一个字节来存储的,7位的编码方案最多只能代表128个字符;经过扩展的8位的编码方案也只能代表256个字符,这远远不能满足计算机发展的需要,对于亚洲国家复杂的字符存储需要更多的码位,于是各种编码方案随之而生。
为了容纳全世界各种语言的所有字符和符号,解决不同编码之间的兼容和转换问题,1991年元月,10多家公司共同出资,组建Unicode协会,随后Unicode编码产生了。
Unicode协会的口号是: 给每个字符提供了一个唯一的数字,不论是什么平台,不论是什么程序,不论什么语言。
最初Unicode编码使用2-Byte(16bit)来进行编码,但是最多只能容纳65536个字符,仍然不够使用,后来进行了扩充,也就是Unicode3.1标准,增加了额外的补充字符定义,现在Unicode4.0标准已经发布,具体可以参考Unicode官方站点:
www.unicode.org
Unicode编码方案主要有三个实施标准:
UTF-8
USC-2
UTF-16
Oracle从7.2开始支持UTF-8编码,提供Unicode编码支持。
按照各种标准的含义,Oracle推荐,如果你的数据库需要存放不同语言的不同符号和字符,建议使用Unicode编码方案。诚然,Unicode方案可以表示更多的字符,但是由于多位的存储,需要额外的存储空间和网络传输,所以选择最适合的数据库字符集仍然需要慎重考虑。
字符集问题的初步探讨(二)-数据库的字符集
字符集在创建数据库时指定,在创建后通常不能更改,所以在创建数据库时能否选择一个正确的字符集就显得尤为重要。
在创建数据库时,我们可以指定字符集(CHARACTER SET)和国家字符集(NATIONAL CHARACTER SET)。
字符集用来存储:
CHAR, VARCHAR2, CLOB, LONG等类型数据
用来标示诸如表名、列名以及PL/SQL变量等
SQL和PL/SQL程序单元等
国家字符集用以存储:
NCHAR, NVARCHAR2, NCLOB等类型数据
这些设置在数据库创建时指定,我们可以看一下数据库的创建脚本:
<>
<>
>>>>>>>>>