知识背景: Oracle的这个参数nls_length_semantics有byte和char两种取值,数据库可以使用这两种单位创建char或varchar2类型的数据库表列。二者的区别是一个按照字符存放,一个按字节存放。 对使用UTF-8字符集的数据库,一个汉字如果按照字节(byte)存放,会占用3个字节(byte),按字符(char)存放一个汉字只占用一个字符(char)。 NLS_LENGTH_SEMANTICS
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. 译: NLS_LENGTH_SEMANTICS 使您能够创建使用字节或字符长度语义的CHAR 和VARCHAR2 列。现有列不受影响。 NCHAR ,NVARCHAR2 ,CLOB ,NCLOB 列总是基于字符。您可能需要使用字节语义,以保持与现有应用程序的兼容性。 NLS_LENGTH_SEMANTICS 不适SYS 和SYSTEM 表。数据字典总是使用字节语义。 1、检查数据库的nls_length_semantics参数值是byte还是char, select value from v$nls_parameters where parameter=’NLS_LENGTH_SEMANTICS’; 注意从:sys.nls_database_parameters里面查到的结果并不是真正的值,使用以上语句查。 说明:某系统对端在建库之后应该调整为Char,而实际上未调整,还是默认的byte类型。 这样,如果要存储的字符串全部是英文字母或数字,那么两者还看不出区别,如果字符串中有汉字,例如“中国”,在定义字段长度的时候,我们定义的是varchar(2),这个NLS_LENGTH_SEMANTICS参数如果为char,那么可以装得下,如果是byte,就装不下了。 当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语义了。 分析: 这个值修改后只对新生成的数据生效,对原有的数据无影响,因此需要想办法把它调整过来: 修改数据库的nls_length_semanitcs参数:该参数有三个级别,分别是database,instance,session。可以分别在nls_database_parameters,nls_instance_parameters,nls_session_parameters里查询到。 数据库级的值在创建数据库时被指定,实例级的值可以通过修改数据库参数pfile或spfile来指定,会话级的可以使用alter session来指定。 实例和会话级的参数只对修改之后的对象(包括字段和plsql变量)产生作用,修改之前的维持不变。具体命令如下: alter system set nls_length_semantics=char scope=both; 注意:此参数必须重启生效,即使可以使用scope=both来修改。 3、由于修改nls_length_segmantics参数后只对修改之后的对象(包括字段和plsql变量)产生作用,修改之前的维持不变,所以还需要对已经存在的对象进行修改。 A、对于字段修改语句如下: alter table tab_name modify col_name varchar2(char_length char); 也可使用下面语句批处理执行(但需要注意在系统空闲的时候使用,对于系统复制需要停止目标端的复制进程): select 'alter table '||owner||'.'||table_name||' modify '||column_name||' VARCHAR2('||char_length||' char);' from dba_tab_columns s where owner IN ('AMBER','KBSD','OWF_MGR','SGPM','SGPM_API','SGPM_OUT','WF_AMBER') and char_used='B' and exists(select 1 from dba_objects t where t.OWNER=s.OWNER and t.OBJECT_NAME=s.TABLE_NAME and t.OBJECT_TYPE='TABLE'); B、对于plsql变量(procedure,package,type等),直接重新编译就可以了,批处理语句如下: select 'alter '||type||' '||name||' compile;' from dba_plsql_object_settings s where s.OWNER in ('AMBER','KBSD','OWF_MGR','SGPM','SGPM_API','SGPM_OUT','WF_AMBER') and nls_length_semantics='BYTE' ; 注意:修改表定义后需要重现编译所有失效对象! 查看所有失效对象并重新编译 SELECT 'ALTER '|| (case OBJECT_TYPE when 'PACKAGE BODY' then 'PACKAGE' else OBJECT_TYPE end) || ' '||o.owner||'.'|| o.object_name|| (case OBJECT_TYPE when 'PACKAGE BODY' then ' COMPILE BODY;' else ' COMPILE;' end) FROM DBA_OBJECTS o WHERE STATUS <> 'VALID' and o.object_type<>'SYNONYM' order by 1; |