Oracle 不同字符集复合索引长度验证


背景

前段时间同事找到一个参数, 可以解决Oracle的char和byte 模式存储超长的问题.
很大程度上解决了研发修改SQL的工作量.
但是发现在某些字符集下面会出现一些异常情况. 
所以想学习和处理一下. 

需要说明我的数据库版本是 Oracle 19.21.0.0
采取多 字符集的PDB模式进行
CDB的字符集是  AL32UTF8
Ora19cutf8 的字符集是 AL32UTF8
Ora19ccesu8 的字符集是 UTF8

参数设置

alter system set nls_length_semantics='BYTE' scope=both;
alter system set nls_length_semantics='CHAR' scope=both;
建议查询方式为:
show parameter SEMAN ;
其他说明: 
这个参数是PDB级别生效的, SYSTEM表空间不受影响. 
注意修改参数建议重启数据库, 如果是PDB, 可以关闭打开PDB就可以, 能够节约时间.

参数说明

NLS_LENGTH_SEMANTICS参数是一个专为创建CHAR和VARCHAR2两种字符型的列时,
指定使用的字节长度,还是使用字符长度的定义方式,有byte和char两种值,默认为byte。

当设置该参数为BYTE时,定义CHAR列或VARCHAR2列采用字节长度方式;
当设置该参数为CHAR时,定义CHAR列或VARCHAR2列采用字符长度的方式。
该参数对于数据库中已经存在的列不具备任何用途,只是在创建表,或修改表的列时才具有意义。

NLS_LENGTH_SEMANTICS参数的值,不对已经存在的列产生任何影响,只是在创建表中的列时,
默认的指定列长度类型为byte还是char,如果在创建或修改表的列时指定了长度类型,
完全覆盖NLS_LENGTH_SEMANTICS参数的值。

测试方法

虽然可以使用修改数据库参数方式进行相关的处理. 
但是根据参数说明里面的 可以再建表的时候 指定 是 char 类型还是 byte 类型
所以其实不需要进行 数据库参数修改了 效率很高.

测试结论

Oracle 的AL32UTF8字符集和UTF8字符集是不太一样的.

在byte 模式下两者的长度限制相同:
单个列长度应该是 6396 可以创建索引
但是如果是两个列的组合索引. 那么长度限制应该是  6393 而不是 6396 这一块需要注意. 

在 char 模式下两者的长度限制不相同
AL32UTF8字符集模式下:
组合索引的两个列的合并长度最大是 1598, 如果是 1599 则会报错
符合: 6393/4=1598.25 取整的结果.

UTF8CESU字符集模式下
组合索引的两个列额最大合并长度是 2797 如果是 2798 只报错
但是这个数据没有找到具体的计算方式. 

另外需要注意, 如果是单列索引. 都可以制作4000长度的索引. 不会出现报错的情况
差异只存在于 复合索引的情况下

测试结果

注意所有的测试结果 都是 zhaobsh103 创建失败, 其他的索引创建成功. 提示错误信息都一致.
ORA-01450: 超出最大的关键字长度 (6397)

AL32UTF8

DROP TABLE  zhaobsh ;
create table zhaobsh (name1 varchar2(1000 char) , name2 varchar2(597 char ),name3 varchar2(598 char ), name4 varchar2(599 char) );
create index zhaobsh101 on zhaobsh(name1,name2) ;
create index zhaobsh102 on zhaobsh(name1,name3) ;
create index zhaobsh103 on zhaobsh(name1,name4) ;

DROP TABLE  zhaobsh ;
create table zhaobsh (name1 varchar2(4000 byte) , name2 varchar2(131 byte ),name3 varchar2(2393 byte ), name4 varchar2(2394 byte) );
create index zhaobsh101 on zhaobsh(name1,name2) ;
create index zhaobsh102 on zhaobsh(name1,name3) ;
create index zhaobsh103 on zhaobsh(name1,name4) ;

UTF8CESU

DROP TABLE  zhaobsh ;
create table zhaobsh (name1 varchar2(2000 char) , name2 varchar2(131 char ),name3 varchar2(797 char ), name4 varchar2(798 char) );
create index zhaobsh101 on zhaobsh(name1,name2) ;
create index zhaobsh102 on zhaobsh(name1,name3) ;
create index zhaobsh103 on zhaobsh(name1,name4) ;

DROP TABLE  zhaobsh ;
create table zhaobsh (name1 varchar2(4000 byte) , name2 varchar2(131 byte ),name3 varchar2(2393 byte ), name4 varchar2(2394 byte) );
create index zhaobsh101 on zhaobsh(name1,name2) ;
create index zhaobsh102 on zhaobsh(name1,name3) ;
create index zhaobsh103 on zhaobsh(name1,name4) ;