关于参数 NLS_LENGTH_SEMANTICS
这个参数的说明可以参见Oracle Online Doc -- http://download-west.oracle.com/docs/cd/B19306_01/server.102/b14237/initparams127.htm
NLS_LENGTH_SEMANTICS
enables you to create CHAR
and VARCHAR2
columns using either byte or character length semantics. Existing columns are not affected.
NCHAR
, NVARCHAR2
, CLOB
, and NCLOB
columns are always character-based. You may be required to use byte semantics in order to maintain compatibility with existing applications.
NLS_LENGTH_SEMANTICS
does not apply to tables in SYS
and SYSTEM
. The data dictionary always uses byte semantics
可以通过如下方式来查看当前Session中的NLS_LENGTH_SEMANTICS的设置情况,
frank @ORCL > show parameter nls_length_semantics
NAME TYPE VALUE
-- ---------------------------------- ----------- --------------------------
nls_length_semantics string BYTE
NLS_LENGTH_SEMANTICS的值默认是byte, 因此在设置varchar2类型的列的大小的时候,如果不显示设置为CHAR, 则默认就是多少个byte的大小,而不是最多可以容纳多少个字符。比如说 varchar2(20)表示最多能容下20bytes的字符串,但是如果是显示设置成Varchar2(20 CHAR)则表示该列能容纳下最多20个字符 (具体占用多少空间决定于数据库所采用的默认字符集, 比如WE8MSWIN1252)。
下面来做个测试,
frank @ORCL > create table tab1(id number ( 10 ), description varchar2 ( 20 ) );
Table created.
frank @ORCL > desc tab1
Name Null ? Type
-- ---------------------------------------------------------------------------------
ID NUMBER ( 10 )
DESCRIPTION VARCHAR2 ( 20 )
现在如果显示设置列description的类型为varchar2(20 char),在来看看在desc表的时候会怎么显示,
frank @ORCL > create table tab2(id number ( 10 ), description varchar2 ( 20 CHAR ));
Table created.
frank @ORCL > desc tab2
Name Null ? Type
-- ---------------------------------------------------------------------------------
ID NUMBER ( 10 )
DESCRIPTION VARCHAR2 ( 20 CHAR )
可以看出这个时候,desc会显示出来列DESCRIPTION的列的类型是VARCHAR2(20 CHAR), 而不是VARCHAR2(20)
如果把当前Session的参数NLS_LENGTH_SEMANTICS设置成CHAR,再来看看desc表的时候会怎么显示,
frank @ORCL > alter session set nls_length_semantics = char ;
Session altered.
frank @ORCL > desc tab1
Name Null ? Type
-- -------------------------------------------------------------------------------
ID NUMBER ( 10 )
DESCRIPTION VARCHAR2 ( 20 BYTE)
frank @ORCL > desc tab2
Name Null ? Type
-- -------------------------------------------------------------------------------
ID NUMBER ( 10 )
DESCRIPTION VARCHAR2 ( 20 )
frank @ORCL >
这次换成了tab1的description列显示地列出来20 BYTE, 而tab2却只显示出20, 因为当前Session字符串大小的单位默认为CHAR,而不是BYTE了
关于字符串处理函数
INSTR, LENGTH, SUBSTR等函数都是作用于character,不管列的类型定义是(xx CHAR) 还是 (xx BYTE)
如果确实想让函数作用于byte,而不是character, 那么可以用这些函数对应的“Byte"版本 -- INSTRB, LENGTHB, SUBSTRB. 每个函数的结尾的是B (byte)
关于如何设置参数 NLS_LENGTH_SEMANTICS
Oracle支持在session 和 system级别来设置这个参数, 如下
alter system set nls_length_semantics = char ;
alter system set nls_length_semantics = byte;
alter session set nls_length_semantics = char ;
alter session set nls_length_semantics = byte;
但是最好不要在system级别来更改这个参数的值。 Tom在他的大作<Expert Oracle Database Architecture>强调这点 ---
You may also use the session or system parameter NLS_LENGTH_SEMANTICS to change the default behavior from BYTE to CHAR. I do not recommend changing this setting at the system level; rather, use it as part of an ALTER SESSION setting in your database schema installation scripts.
Any application that requires a database to have a specific set of NLS settings makes for an “unfriendly” application. Such applications, generally, cannot be installed into a database with other applications that do not desire these settings, but rely on the defaults to be in place.
(--by Tom)
另外,还有一点需要注意的是,Oracle数据库表最多支持4000byte的 varchar2列,即使在创建表的时候指定为varchar2(4000 char), 还是不可以往该列中插入超过4000byte的字符串。 --
One other important thing to remember is that the upper bound of the number of bytes stored in a VARCHAR2 is 4,000. However, even if you specify VARCHAR2(4000 CHAR), you may not be able to fit 4,000 characters into that field. In fact, you may be able to fit as few as 1,000
characters in that field if all of the characters take 4 bytes to be represented in your chosen character set! (-- by Tom)
提到varchar2,自然要提下char,varchar2最大支持4000个字节长度的字符串,char只能支持最多2000个字节长度的字符串。这里讲的都是在Oracle数据库中的限制,区别于PL/SQL中的varchar2 (支持最多32K大小的字符串)
VARCHAR2/CHAR的N(national)的变种 -- NVARCHAR2/NCHAR
NVACHAR2跟VARCHAR2最大的区别在于:
(1)NVARCHAR2类型的字符串是按照数据库的国家字符集(national character set -- NLS_NCHAR_CHARACTERSET) 编码方式存储的,而不是用数据库默认字符集 (NLS_CHARACTERSET) 编码方式存储的
可以通过如下方式查询当前数据库的的默认字符集和国家字符集,
frank @ORCL > select * from nls_database_parameters where parameter in ( ' NLS_CHARACTERSET ' , ' NLS_NCHAR_CHARACTERSET ' );
PARAMETER VALUE
-- ---------------------------- ----------------------------------------
NLS_CHARACTERSET WE8MSWIN1252
NLS_NCHAR_CHARACTERSET AL16UTF16
frank @ORCL >
(2) NVARCHAR2的长度是以character(字符)来度量的,不像varchar2那样可以设置CHAR或者BYTE.
从Oracle 9i开始 ,数据库国家字符集可以是以下两种之一: UTF8或者AL16UTF16 (9i是UTF16, 10g是AL16UTFf16), 这就使得NVARCHAR2只适合存储多字节的字符。 在Oracle 9i之前,Oracle允许用户来选择任意的字符集作为国家字符集。
下图摘取自Oracle Globalization Support Guide, 可以看出NVARCHAR2只支持多字节的Unicode编码方式。
关于CHR函数
CHR()用来返回给定数值在ASCII编码表中对应的值(一个字节),超过256的会返回改数值对256取余结果对应的ASCII码。
(
其他测试:
首先创建3个表t1, t2, t3,它们都只包含一列a,只是该列的类型不同,分别为varchar2(2 byte), varchar2(2 char), nvarchar2(2). 注意nvarchar2不能设置byte或char, 因为它只允许char,因此没有必要显示设置。
frank @ORCL > create table t1(a varchar2 ( 2 byte));
Table created.
frank @ORCL > create table t2(a varchar2 ( 2 char ));
Table created.
frank @ORCL > create table t3(a nvarchar2( 2 byte) );
create table t3(a nvarchar2( 2 byte))
*
ERROR at line 1 :
ORA - 00907 : missing right parenthesis
frank @ORCL > create table t3(a nvarchar2( 2 char ) );
create table t3(a nvarchar2( 2 char ))
*
ERROR at line 1 :
ORA - 00907 : missing right parenthesis
frank @ORCL > create table t3(a nvarchar2( 2 ));
Table created.
frank @ORCL >
现在分别向这三张表里插入一个汉字‘我’, 如下,
frank @ORCL > insert into t1(a) values ( ' 我 ' );
1 row created.
frank @ORCL > insert into t2(a) values ( ' 我 ' );
1 row created.
frank @ORCL > insert into t3(a) values ( ' 我 ' );
1 row created.
frank @ORCL >
因为数据库字符集和国家字符集分别是WE8MSWIN1252, AL16UTF16 (如下所示),因此表t1, t2和t3中对汉字'我'存储肯定不一样,因为nvarchar2用的是国家字符集编码方式,而varchar2用的是数据库字符集。
frank @ORCL > select * from nls_database_parameters where parameter in ( ' NLS_CHARACTERSET ' , ' NLS_NCHAR_CHARACTERSET ' );
PARAMETER VALUE
-- ---------------------------- ----------------------------------------
NLS_CHARACTERSET WE8MSWIN1252
NLS_NCHAR_CHARACTERSET AL16UTF16
测试如下,
frank @ORCL > select length(a), lengthB(a), dump (a) from t1;
LENGTH(A) LENGTHB(A) DUMP (A)
-- -------- ---------- ------------------------------
2 2 Typ = 1 Len = 2 : 206 , 210
frank @ORCL > select length(a), lengthB(a), dump (a) from t2;
LENGTH(A) LENGTHB(A) DUMP (A)
-- -------- ---------- ------------------------------
2 2 Typ = 1 Len = 2 : 206 , 210
frank @ORCL > select length(a), lengthB(a), dump (a) from t3;
LENGTH(A) LENGTHB(A) DUMP (A)
-- -------- ---------- ------------------------------
2 4 Typ = 1 Len = 4 : 0 , 206 , 0 , 210
frank @ORCL >
可以看到一个汉字'我'在数据中是用当两个字符存储的(从length的返回值看出),对于t1, t2还是可以理解,毕竟‘我’占用了两个字节(可以从t1,t2的lengthB返回的结果看出),而WE8MSWIN1252是用一个字节来表示一个字符,因此oracle可能进行了一些处理,将'我‘处理成两个字符来存储.(个人猜测)
但是对于t3的返回结果就不是很好理解了,因为nvarchar2用的是 AL16UTF16字符集编码,两个字节的汉字完全可以用跟一个unicode字符来表示的,但是结果却是显示2 (如果把t3的a类型改成NVARCHAR(1), 就会发现'我’根本插入不进去,因为空间不够)(why??),lengthb返回的结果就好理解了,因为一个字符用2个字节表示,自然最后返回的结果就是4个字节了。
-- 还有待于进一步的求证---