标签: 杂谈 |
分类:oracle |
作为一个成熟的商业数据库软件,Oracle对全球化(Global Support)有着全面的支持和解决方案(即用Oracle NLS架构来实现的)。Oracle在国际化支持方面主要体现在几个方面:
ü 全球化字符集支持;目前,Oracle支持所有主流的字符集样式。通过NLS_CHARACTER和NLS_NCHAR_CHARACTER指定的字符集可以支持全世界绝大多数语言文字的存储。Oracle推荐的字符集样式为AL32UTF8,也就是通常我们所见的UTF-8编码格式;
ü 全球化时区Timezone支持;全球化一个重要问题就是时区显示和保存问题。不同时区的用户同时向一个数据库插入数据,其时间先后是不可能回避的问题。同时,当非本地时区日期类型显示的时候,时区如何进行转换。Oracle在这个问题上提供了两个类型:Timestamp with timezone和Timestamp with local timezone,方便的解决了这些问题;
ü 日期和数字格式显示问题;日期和数字类型的显示,带有很强烈的地区特性。比如,欧美格式“17-SEP-12”和我们更接受的“2012-9-17”就有很大的差距。Oracle将数据取值和显示分割开来,让session级别用户可以控制最终的显示;(ü 软件显示语言;在Oracle软件和一些显示中,很多的字符和软件输出就带有语言特性。比如,在我们在英文版的Oracle服务端,调用时间函数而获得结果为“17-SEP-12”,而在笔者的客户端系统中,调用时间函数而获得结果显示为“17-9月-12”。这都是Oracle NLS国际化所起到的作用;)
ü 软件显示语言;在Oracle软件和一些显示中,很多的字符和软件输出就带有语言特性。比如,在我们在英文版的Oracle服务端,调用时间函数而获得结果为“17-SEP-12”,而在笔者的客户端系统中,调用时间函数而获得结果显示为“17-9月-12”。这都是Oracle NLS国际化所起到的作用;
NLS是一套覆盖面广、内容庞杂的知识体系。笔者计划在接下来用一个系列来分析这个体系。本次,我们只是给NLS一个简单的体系介绍:Oracle的三层NLS体系关系和优先级配置。
1、环境介绍
笔者选择Oracle 11gR2进行试验。
SQL> select * from v$version;
BANNER
---------------------------------------
Oracle Database 11g EnterpriseEdition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 – Production
服务器是Red Hat 5.3 32bit,安装时选择的是英文,美语。
[oracle@bspdev ~]$ uname -a
Linux bspdev.localdomain 2.6.18-308.el5 #1 SMP Tue Feb 21 20:05:41EST 2012 i686 i686 i386 GNU/Linux
查看Linux系统支持语言和时区:
[oracle@bspdev ~]$ cat /etc/sysconfig/i18n
LANG="en_US.UTF-8"
SYSFONT="latarcyrheb-sun16"
[oracle@bspdev sysconfig]$ pwd
/etc/sysconfig
[oracle@bspdev sysconfig]$ cat clock
# The ZONE parameter isonly evaluated by system-config-date.
# The timezone of the system is defined by the contents of/etc/localtime.
ZONE="Asia/Shanghai"
UTC=true
ARC=false
客户端使用的是中文的Windows 7,对应的Oracle注册表内容为:
NLS_LANG参数告知本地所在地中国、中文语言和ZHS16GBK字符集。
2、三层NLS体系
Oracle的Global Support复杂的原因之一,就是Oracle的三层NLS体系。任何一个会话session在连接入数据库之后,都会面对三层NLS参数体系(即每一层都有一套自己的与其他层中同名的NLS参数变量集合。Database level有一个完备的NLS参数集合中各变量的值,这样很大层面上为后面两层提供了一个基础参数集合和存储标准,即当Instance Level里,用户没有为其NLS参数设置值时,则默认用Database level的给其赋值,当Session Level 里,用户没有为其NLS参数设置值时,则默认用Instance Level的给其赋值),分别为:Database、Instance和Session。
ü Database Level NLS Parameter:数据库层面的NLS参数是在数据库创建的时候确定的一系列的参数。在创建数据库的时候,我们都可以通过OUI(Oracle OUI 安装程序,即用于创建一个新的数据库的那个程序)或者responseFile(?)进行配置。大部分Database Level NLS Parameter都是不可以改变,或者不能轻易改变的,如CharacterSet;
ü Instance Level NLS Parameter:在数据库运行过程中,一些NLS参数是通过Spfile/Pfile参数文件进行配置,并且可以对Database Level NLS进行一定的覆盖修改。如果Instance Level与Database Level的发生冲突,以Instance Level覆盖Database Level的配置;
ü Session Level NLS Parameter:这个层面是和用户连接效果最直接的层面。用户使用的NLS参数很多都是取到这个层面的参数。Session Level Parameter来自客户端配置内容(所以,Session Level NLS Parameter确切的名字应该叫做 客户端 Level NLS Parameter。一个客户端可以有很多会话同时存在的),主要是通过一系列的环境变量来确定。Session Level的Parameter是可以覆盖Instance Level的参数的;
下面我们分别来介绍几个层面参数中的重要内容。
3、Database Level NLS Parameter
Database level参数是三层体系中最底层,这个层面的参数取值和Oracle数据库创建时的配置选项密切相关。我们在联入数据库时,可以通过几个视图来查看这个“根本性”的参数内容。
SQL> select * fromnls_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.SSXFFAM
NLS_TIMESTAMP_FORMAT DD-MON-RRHH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFFAM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RRHH.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.1.0
20 rows selected
nls_xxx_parameters系列视图非常重要,特别是在我们配置NLS参数的过程中。我们可以通过nls_database_parameters查看到Database的NLS参数。
NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET用于表示Oracle在保存varchar2/char和nvarchar2/nchar字段时的保存类型。从Oracle官方推荐的角度,我们设置AL32UTF8字符集作为数据库字符集,基本上就可以应对常见的字符文字类型了。
在安装程序OUI运行时,AL32UTF8往往不是默认选项。Oracle OUI程序(Oracle OUI 安装程序,即用于创建一个新的数据库的那个程序)会根据所在服务器操作系统的配置内容,为我们选择出一个字符集(即创建数据库的界面里,选择字符集那一步,有个“操作系统默认字符集”选项)。
在从操作系统选择出字符集合的过程中,NLS_LANG(LANG)环境变量起到很重要的作用(即“操作系统默认字符集”选项的值就是来自NLS_LANG(LANG)环境变量中的. 操作系统的语言配置内容)。NLS_LANG是我们安装Oracle(即创建一个新的Oracle数据库)过程中,确定NLS参数的一个重要环境变量。
根据Oracle的要求,NLS_LANG中包括了Oracle所在地域Territory、语言Language和字符集CharacterSet三部分内容。
NLS_LANG=_.
如果我们需要在配置Oracle(即创建一个新的Oracle数据库)前就确定这些(Database Level )NLS参数内容,可以预先定义环境变量NLS_LANG(?)(即创建一个新的Oracle数据库时,该数据库Database Level NLS参数中的NLS_LANGUAGE、NLS_TERRITORY 、NLS_CHARACTERSET三个变量的值来自客户端环境变量NLS_LANG的赋值)。Unix/Linux环境下,可以配置在Oracle用户的.bash_profile中。而Windows环境下,这个参数要配置在注册表中。
此外,NLS_DATA_LANGUAGE用来表示Oracle在显示日期类型的文字内容时,使用什么语言。
Database Level NLS parameter是非常基本的,起作用的范围主要数据库内部。另一方面,Database Level NLS Parameter是作为Instance Level配置的默认基础值。在安装数据库后,我们基本不能、也不会对database level的NLS参数进行修改。我们的“手脚”只能在Instance和Session level进行。
3、Instance level NLS Parameter
Instance Level NLS parameter,就是我们可以控制的领域了。Instance参数主要来自于参数文件SPFILE/PFILE。我们可以通过nls_instance_parameters来查看这个层面的参数配置。
SQL> select * fromnls_instance_parameters;
PARAMETER VALUE
------------------------------ -------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_SORT
NLS_DATE_LANGUAGE
NLS_DATE_FORMAT
NLS_CURRENCY
NLS_NUMERIC_CHARACTERS
NLS_ISO_CURRENCY
NLS_CALENDAR
NLS_TIME_FORMAT
NLS_TIMESTAMP_FORMAT
NLS_TIME_TZ_FORMAT
NLS_TIMESTAMP_TZ_FORMAT
NLS_DUAL_CURRENCY
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
17 rows selected
相对于database level,Instance level的参数数量是很少的。通常我们不会在参数文件层面进行NLS参数配置,保持Database Level的默认配置就可以了。
这样的原因是Instance Level配置是针对所有连接而言的。而国际化的目的就是在于不同连接用户,看到Local化的结果。无论Instance Level进行何种的配置,最终很有可能都是被Session level的所覆盖。
Database level之所以会有完备的NLS参数,很大层面上是提供了一个基础参数集合和存储标准。NLS的重头在session层面。
4、Session Level NLS Parameter
Session Level的NLS参数是和用户最直接交互的部分。我们可以通过nls_session_parameters视图查看到这个内容(Session level的NLS参数来自客户端配置,故nls_session_parameters视图里的内容即来自客户端配置)。
SQL> select * fromnls_session_parameters;
PARAMETER VALUE
------------------------------ -------------------------
NLS_LANGUAGE SIMPLIFIEDCHINESE
NLS_TERRITORY CHINA
NLS_CURRENCY ¥
NLS_ISO_CURRENCY CHINA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE SIMPLIFIEDCHINESE
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFFAM
NLS_TIMESTAMP_FORMAT DD-MON-RRHH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFFAM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RRHH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY ¥
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
17 rows selected
session level的参数是影响最终我们看到NLS结果的控制因素。Session level的NLS参数来自客户端配置。对Windows而言,就是我们注册表中关于NLS_LANG等一系列的环境变量。对Unix/Linux而言,就是我们配置在.bash_profile文件中定义的相应内容。
注意,NLS_LANG本身包括的内容很多。NLS_LANG包括了客户端地域、语言和字符集信息。地域间接影响到时区,语言会影响到显示语言。而字符集更是影响数据从服务器传递过来客户端后,进行的转换策略。
在三层NLS体系下,Session Level的Parameter起到最后的决定作用。如果Session level没有配置,Oracle会选择Instance乃至Database的配置(来作为Session Level的Parameter的默认值)。Session Level的配置会覆盖Instance level的取值。
我们可以根据自己的情况,动态的进行调节session level nls parameter。
如果我们只是希望NLS暂时性修改(即在当前会话有效),可以选择alter session set语句进行参数切换。
SQL> select sysdate from dual;
SYSDATE
--------------
17-9月 -12
SQL> alter session set nls_date_format='yyyy-mm-ddhh24:mi:ss';
会话已更改。
SQL> select sysdate from dual;
SYSDATE
-------------------
2012-09-17 06:23:58
如果希望长期的修改,可以考虑在(客户端的)环境变量的层面上修改参数。在windows上,可以添加注册表参数。
SQL> select * from nls_session_parameters whereparameter='NLS_DATE_FORMAT';
PARAMETER VALUE
------------------------------ -------------------------
NLS_DATE_FORMAT yyyy-mm-ddhh24:mi:ss
SQL> conn sys/oracle@wilson as sysdba
已连接。
SQL> select sysdate from dual;
SYSDATE
-------------------
2012-09-17 06:29:02
5、结论
NLS参数对我们开发和使用Oracle,至关重要!
后记:
NLS_LANGUAGE影响oracle错误消息等在客户端中由oracle数据库(软件)服务端发来的要显示的消息(而非客户端本身所要显示的信息),再如splplus中的“表已创建、连接到”等显示的信息就是如此。NLS_LANGUAGE不影响表中(所有)数据的显示,具体的说,就是不影响表中varchar2/char和nvarchar2/nchar字段的数据的显示(,还有显示日期类型的表字段)。
而表中varchar2/char和nvarchar2/nchar字段的数据的显示由NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET决定,即NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET用于表示Oracle在保存varchar2/char和nvarchar2/nchar字段时的保存类型。
NLS_DATA_LANGUAGE用来表示Oracle在显示日期类型的(的表字段里的)文字内容时,使用什么语言(的字符)。
至于NLS_LANGUAGE用什么字符集由oracle数据库软件决定,而NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET则可以由oracle用户来自定义。
NLS参数最终被覆盖的值为oracle数据库(软件)服务端所用?
客户端的环境变量nls_lang
NLS_LANG参数由以下部分组成:
NLS_LANG=
所以,客户端的nls_lang设置后会覆盖影响oracle数据库(软件)服务端nls架构模块功能程序中的session level的以下三个参数变量的数值:
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CHARACTERSET
session level的NLS参数应该存放在各个UGA中。
客户端都是基于服务端程序提供的接口(来访问操作服务端程序的)而开发出来的,所以客户端是否需要某个环境变量是取决于服务端程序是否要用到该变量。故而,在其他非基于oracle数据库(软件)服务端程序提供的接口而开发出来的客户端是不需要用到环境变量nls_lang的。
除了NLS_CHARACTERSET变量外的NLS变量,都是以session level的该NLS参数值为准来控制显示结果。而NLS_CHARACTERSET变量则貌似要客户端和数据库端配合来控制显示结果?
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CHARACTERSET
这三个参数是相互独立的,即其各自影响的数据不会有重合部分。
===============================================================================
NLS_LANGUAGE影响oracle错误消息等在客户端中由oracle数据库(软件)服务端发来的要显示的消息(而非客户端本身所要显示的信息),再如splplus中的“表已创建、连接到”等显示的信息就是如此。NLS_LANGUAGE不影响表中(所有)数据的显示,具体的说,就是不影响表中varchar2/char和nvarchar2/nchar字段的数据的显示(,还有显示日期类型的表字段)。
而表中varchar2/char和nvarchar2/nchar字段的数据的显示由NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET决定,即NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET用于表示Oracle在保存varchar2/char和nvarchar2/nchar字段时的保存类型。
NLS_DATA_LANGUAGE用来表示Oracle在显示日期类型的(的表字段里的)文字内容时,使用什么语言(的字符)。
至于NLS_LANGUAGE用什么字符集由oracle数据库软件决定(根据NLS_LANGUAGE所指定的语言,oracle数据库软件服务端选择该语言下的一个字符集),而NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET则可以由oracle用户来自定义。
NLS参数最终被覆盖的值为oracle数据库(软件)服务端所用?
客户端的环境变量nls_lang
NLS_LANG参数由以下部分组成:
NLS_LANG=
所以,客户端的nls_lang设置后会覆盖影响oracle数据库(软件)服务端nls架构模块功能程序中的session level的以下三个参数变量的数值:
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CHARACTERSET
session level的NLS参数应该存放在各个UGA中。
客户端都是基于服务端程序提供的接口(来访问操作服务端程序的)而开发出来的,所以客户端是否需要某个环境变量是取决于服务端程序是否要用到该变量。故而,在其他非基于oracle数据库(软件)服务端程序提供的接口而开发出来的客户端是不需要用到环境变量nls_lang的。
除了NLS_CHARACTERSET变量外的NLS变量,都是以session level的该NLS参数值为准来控制显示结果。而NLS_CHARACTERSET变量则貌似要客户端和数据库端配合来控制显示结果?
Session Level NLS Parameter和客户端nls环境变量集合(存在于注册表中)如nls_lang是两套不同的变量集合。这个从select * fromnls_session_parameters;中不存在NLS_CHARACTERSET一行,而客户端环境变量nls_lang里有NLS_CHARACTERSET,而可知。
Database Level NLS Parameter、InstanceLevel NLS Parameter、Session Level NLS Parameter间类似于类继承关系。NLS_CHARACTERSET是DatabaseLevel NLS Parameter的私有成员变量,不可被Session Level NLS Parameter继承访问。当然,在Session Level里可以被继承访问的那些NLS Parameter的值在最终local化里起作用(,而不是Database Level或是Instance Level NLS Parameter的值)。
如果用户设置了某一客户端nls环境变量如nls_lang,则其值就会自动赋值给Session Level NLS Parameter里相应的NLS参数。
由于NLS_CHARACTERSET变量在Session Level里不存在的,所以客户端环境变量nls_lang里的NLS_CHARACTERSET是要和Database Level NLS Parameter NLS_CHARACTERSET一起配合共同起作用。
即凡是在Session Level里不存在的变量,客户端环境变量里的该同名变量都是要和Database Level NLS Parameter里该同名变量一起配合共同起作用。
凡是在Session Level里存在的变量,则由其单独起作用。
altersession set NLS_CHARACTERSET 提示 ora_000922:missing orinvaild option
说明NLS_CHARACTERSET不可在会话级别被修改。
select userenv('language') from dual;
和selectsys_context('userenv','language') from dual;
都是显示 session Level NLS Parameter的值,其中就有NLS_CHARACTERSET的值,所以说明NLS_CHARACTERSET是Database Level NLS Parameter的私有成员变量,被SessionLevel NLS Parameter继承却不可访问(即不可被修改)。
selectuserenv('lang') from dual;显示 session Level NLS Parameter NLS_LANGUAGE的值。
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CHARACTERSET
这三个参数是相互独立的,即其各自影响的数据不会有重合部分。
客户端nls_lang里的字符集,对在客户端里输入的SQL语句文本(的编码值),会将其当作是客户端nls_lang里的字符集编码的编码值来解释解析,而不会对其编码值进行转码为客户端nls_lang里的字符集编码的编码值。例如,输入的SQL语句文本的编码值(客户端字符集,作用的地方(?))采用的是ZHS16GBK字符集的,而客户端nls_lang里的字符集为AL32UTF8时,执行该SQL语句,提示ora911:无效字符,说明未转码。
客户端nls_lang里的字符集、客户端字符集是两个不同的变量,都是属于客户端的。
客户端环境变量nls_lang指定无效值,如写个无效的字符集名时,提示
ORA-12705:Cannot access NLS data files or invalid environment specified
NLS_LANG值,则会根据所在服务器操作系统的配置内容,为我们选择出一个字符集
再论ORACLE的全球化支持(GLOBALIZATION)
=====================================
KOREAN_CHINA.KO16KSC5601
SIMPLIFIED CHINESE_CHINA.AL32UTF8
设计客户端nls_lang这个变量的作用是讲oracle服务端发来的所有字符串(包括错误消息字符串和表里字符串字段)的以服务端数据库字符集
编码的编码值转码为客户端字符集(狭义,即操作系统字符集)的编码值
故客户端nls_lang要和客户端字符集一致才好。客户端nls_lang要和客户端字符集间不转码,即设置为客户端字符集的客户端输出函数不转码
地显示以客户端nls_lang编码的
字符串为参数的值(从如下例子可知的:客户端nls_lang为ZHS16GBK,客户端字符集cmd 为代码页437时 如 表里的中文数据为乱码,而非?
字符,即可知道这里未发生转码)。
表里char数据类型的字符串字段 在 服务端数据库里以database级别的nls_charset字符集编码的
表里nchar数据类型的字符串字段 在 服务端数据库里以database级别的nls_ncharset字符集编码的
错误消息字符串等由session级别的nls_language决定,而nls_language值对应用的字符集由oracle服务端软件决定
oracle服务端发来的错误消息字符串 根据客户端nls_lang 转码
其他不变 服务端数据库字符集为ZHS16GBK 客户端字符集cmd GBK
客户端nls_lang
KOREAN_CHINA.KO16KSC5601
KOREAN_CHINA.AL32UTF8
显示乱码字符不一样,说明有转码
再如,
其他不变 服务端数据库字符集为ZHS16GBK 客户端字符集cmd GBK(即ZHS16GBK)
客户端nls_lang
SIMPLIFIED CHINESE_CHINA.US7ASCII
显示??字符,说明有转码(源字符集的字符不存在目标字符集里时,一般转码为?字符)
oracle服务端发来的错误消息字符串是否按
服务端数据库字符集编码的?
服务端数据库字符集为ZHS16GBK还是AL32UTF8
客户端nls_lang为ZHS16GBK时不乱码;
客户端nls_lang为AL32UTF8时乱码;
客户端字符集cmd一直是 GBK
这个例子还不能证明上述结论,因为
(客户端nls_lang为)ZHS16GBK 和(服务端数据库字符集为)AL32UTF8间(共有的字符)是可正确转码的,
而客户端nls_lang为AL32UTF8时客户端cmd的字符集为 GBK,故最后显示为乱码。
服务端数据库字符集为ZHS16GBK 客户端字符集cmd 韩文
客户端nls_lang
KOREAN_CHINA.KO16KSC5601
不乱码 说明oracle服务端发来的错误消息字符串不按
服务端数据库字符集编码的,而是
错误消息字符串等由session级别的nls_language决定,而nls_language值对应用的字符集由oracle服务端软件决定。