一、问题描述
数据库大表冷热数据分离,需要把实例A的数据迁移到实例B, 由于表有300多G, 综合评估后,计划先在B实例创建相同表结构的表,然后通过dblink结合存储过程分批insert完成数据迁移;
但是在迁移数据的时候报错,字段超长,但是明明我是按着原表表结构创建的归档表,并且字符集也一样,为啥会报错?
二、问题分析
原来是生产实例修改了参数
NLS_LENGTH_SEMANTICS=char;
介绍下oracle NLS_LENGTH_SEMANTICS参数作用:
当NLS_LENGTH_SEMANTICS参数被设置为CHAR时,VARCHAR2(100)的定义就意味着这个字段可以存储最多100个字符的数据,而不论这些字符在数据库的字符集编码下占用多少个字节。
NLS_LENGTH_SEMANTICS=BYTE(默认情况),VARCHAR2(100)表示字节 ,最多可以存100个字节,当字符集为UTF8的时候,一个汉字占三个字节,所以最多可以存33个中文汉字;
注意事项:
1、参数对于数据库中已经存在的列不具备任何用途,只是在创建表,或修改表的列时才具有意义;
2、必须需要重启才会生效!(就算指定了scope=both);
3、当
NLS_LENGTH_SEMANTICS=char的时候,新创建的表 VARCHAR2(100)表示100个字符的意思,但是你通过plsql developer查看表结构的创建语句,看不到任何区别,然而你通多 sqlplus 登录数据库,通过desc table_name可以看到表结构中VARCHAR2(100) 变成了 VARCHAR2(100 char);
顺便介绍下MySQL和Oracle中关于varchar类型区别:
- Oracle:允许通过NLS_LENGTH_SEMANTICS参数选择是按字节还是按字符指定CHAR和VARCHAR2列的长度。
- MySQL:CHAR和VARCHAR类型的长度直接以字符为单位指定,无需额外设置;
字符集为UTF8时,MySQL的varchar(10) 表示可以存10个字符,Oracle的varchar2(10) 默认情况下表示可以存10个字节的意思。
三、问题处理:
方法1:修改归档数据库参数:
ALTER SYSTEM SET NLS_LENGTH_SEMANTICS=CHAR scope=spfile;
重启数据库才能生效,尽管scope=both,也需要重启数据库;
方法2:创建归档表的时候指定VARCHAR2(100 char),添加char关键字;
小结:
Oracle数据库多为关系型数据库的扛把子,在参数配置上更灵活,但是可能会给不熟悉相关配置的人带来困扰;