从9i开始,oracle提供了NLS_LENGTH_SEMANTICS这个参数,其有两个取值,CHAR和BYTE。当为CHAR时字符类型的长度是按字符个数来计算,而不是按BYTE来计算,这在使用变长字符集(AL32UTF8)的情况下非常有用,因为一个字符所占用的字节数是不定的,就给我们准确估计字段长度(BYTE)带来不便。同时当为CHAR时,对那些采用7/8bit的字符集(US7ASCII/WE8MSWIN1252)来说也不会带来空间上的浪费。
下面就使用使用该参数需要注意的一些地方做出描述
1、该参数分为三个级别,分别是数据库、实例、会话三个级别
可以分别在NLS_DATABASE_PARAMETERS、NLS_INSTANCE_PARAMETERS、NLS_SESSION_PARAMETERS里查询到
数据库级的值在创意数据库时被指定,实例级的值可以通过修改init.ora/Spfile来指定,会话级的可以使用alter session来指定
2、实例/会话级的的参数只对其修改之后的对象(包括字段和pl/sql变量)产生作用,修改之前的维持不变。
可以使用ALTER SYSTEM SET NLS_LENGTH_SEMANTICS=CHAR scope=spfile;来修改实例级的参数值
注意,必须需要重启才会生效!(就算指定了scope=both)
该参数取值决定顺序
1、如果没有客户端(环境变量,后面会讲到)或者会话级指定该值,该参数由实例级的值决定(通过NLS_INSTANCE_PARAMETERS查询)
2、从10g开始,NLS_LENGTH_SEMANTICS可以在环境变量或者注册表中设定,一旦设定之后会话级的值默认为环境变量或注册表中的取值
(通过NLS_SESSION_PARAMETERS查询)
3、可以使用ALTER SESSION SET NLS_LENGTH_SEMANTICS=CHAR改变当前会话的取值。
其它注意事项
Oracle E-business Suite不支持该参数的CHAR语义
在11g以下的数据库中,不要在创建数据库时指定 NLS_LENGTH_SEMANTICS=CHAR,而应该使用 NLS_LENGTH_SEMANTICS=BYTE(或者不指定)来创建数据库,然后在修改ini.ora/spfile里改参数的值。否则会导致数据创建期间部分XDB和SYS的对象使用不支持的CHAR语义,产生错误,不过11.1.0.6之后已经得到解决。详见 Bug 5545716 和 Bug 4886376
数据库级取值为BYTE,实例级取值为CHAR 和 数据库级取值为CHAR,实例级取值为CHAR 是相同的,所以没必要纠结,详情可以参阅 Note 241047.1 The Priority of NLS Parameters Explained.
你需要在BYTE语义下调用使用了STARTUP MIGRATE的脚本(比如patch脚本或者 $ORACLE_HOME/RDBMS/ADMIN 下的脚本(比如catalog.sql ))
If you run patch scripts or scripts from $ORACLE_HOME/RDBMS/ADMIN like catalog.sql use STARTUP MIGRATE; and run then the scripts. ( run them with NLS_LENGTH_SEMANTICS=BYTE )
* Oracle Text does NOT support instance wide NLS_LENGTH_SEMANTICS=CHAR If you are using Oracle Text then the database needs to be created and started with NLS_LENGTH_SEMANTICS=BYTE.
Using CHAR semantics on column definitions itself is supported however.Bug 4465964
This is not documented in the 10.2 docset, it is in the 11g doc: http://download.oracle.com/docs/cd/B28359_01/text.111/b28304/csql.htm#i997737 section "CHARSET COLUMN charset_column_name"
9i以下的客户端不能识别CHAR语义,当你用8i客户端去连接9i UTF8数据库时,数据库的VARCHAR2(10 CHAR)会以BYTE语义显示为VARCHAR2(30)
这样8i的的客户端才能插入不超过30byte的数据,鉴于此,当使用CHAR语义时,最好使用9i以上的客户端。
可以使用以下方法限定客户端版本为9及以上:
在10.1 中可以在init.ora中指定DB_ALLOWED_LOGON_VERSION=9
10.2及以上,在sqlnet.ora中指定SQLNET.ALLOWED_LOGON_VERSION = 9
注意:不要在10.2及以上使用DB_ALLOWED_LOGON_VERSION
设置之后,如果用8i及以下的客户端访问数据库会报ORA-28040: No matching authentication protocol错误
默认值
当NLS_LENGTH_SEMANTICS取值为BYTE时,默认为BYTE;当取值为CHAR时,默认为CHAR。
意思就是说,如果NLS_LENGTH_SEMANTICS=BYTE char(10)实际上就是 char(10 byte)
如果NLS_LENGTH_SEMANTICS=CHAR char(10)实际上就是 char(10 CHAR)
不管NLS_LENGTH_SEMANTICS取值为何,都可以在使用时显示的指定是按CHAR还是BYTE
例如,实例NLS_LENGTH_SEMANTICS=BYTE,在创建表时可以指定char(10 char)就可以使用char语义了
另外,对于单字节字符集编码来说,CHAR=BYTE
NLS_LENGTH_SEMANTICS对于属于SYS的表(对SYSTEM有效)无效,如下:
SQL> ALTER SESSION SET NLS_LENGTH_SEMANTICS=CHAR; Session altered. SQL> Create table sys.test1 (Col1 CHAR(20),Col2 VARCHAR2(100)); Table created. SQL> Create table sys.test2 (Col1 CHAR(20 CHAR),Col2 VARCHAR2(100 CHAR)); Table created. SQL> desc test1 Name Null? Type ----------------------------------------- -------- ---------------------------- COL1 CHAR(20 BYTE) COL2 VARCHAR2(100 BYTE) SQL> desc test2 Name Null? Type ----------------------------------------- -------- ---------------------------- COL1 CHAR(20) COL2 VARCHAR2(100)
PL/SQL与NLS_LENGTH_SEMANTICS
存储过程和包里的在创建或者重新编译的时候会读取当前SESSION的NLS_LENGTH_SEMANTICS值,并存入代码中。考虑下面的问题
当前会话使用的是BYTE语义,你创建了一个存储过程,里面有一个变量的类型声明为CHAR(10),那么此时该变量实际为CHAR(10 BYTE),
有一天你把BYTE改成了CHAR,然后去调用该存储过程,你会发现报 ORA-06502 PL/SQL Numeric or value error错误,因为虽然环境使用的是CHAR语义,
但是存储过程里还是使用的BYTE语义,此时你需要重要编译该存储过程,它后重新读取当前SESSION的值,写入代码中。
鉴于此,建议在书写存储过程的时候显示声明其语义(CHAR(10 CHAR)),以免不必要的麻烦。
可以通过下在的SQL来查看现有存储过程使用的语义
SELECT C.owner ||'.' || C.name ||' - ' || C.type FROM DBA_PLSQL_OBJECT_SETTINGS C WHERE C.NLS_LENGTH_SEMANTICS = 'CHAR' -- to have a list of all using BYTE semantics use -- WHERE C.NLS_LENGTH_SEMANTICS = 'BYTE' -- to check only for a certain users use -- and C.owner IN ('SCOTT') ORDER BY C.owner,C.name