揭秘达梦数据库初始化参数
- 1. 问题描述
- 2. 初始化参数 (critical)
- 2.1 字符集 CHARSET/UNICODE_FLAG
- 2.2 大小写敏感 CASE_SENSITIVE
- 2.3 varchar类型长度以字符为单位 LENGTH_IN_CHAR
- 2.4 空白字符填充模式 BLANK_PAD_MODE
- 2.5 页大小 PAGE_SIZE
- 3. 个人经验
1. 问题描述
由于最近频繁遇到客户那边自己初始化库,并且他们平常根本没太注意初始化参数,导致使用DTS迁移工具迁移数据库时,报各种错误信息。例如:行记录超长、字符串被截断、列超长、字符乱码、无效的表或视图、含字母大小写的字符串比较、含空格的字段处理方式等等。这些错误基本上可以初始化库时准备齐全,减少不必要的麻烦。
为弄清达梦数据库关于几个重要初始化参数的作用,以下篇幅将进行详细阐述和举例。切记:初始化库的参数在建库后不支持手工修改,若想支持某一个功能,须按自己的参数需求重新初始化库。
( charset / case_sensitive / length_in_char / blank_pad_mode / page_size )
2. 初始化参数 (critical)
2.1 字符集 CHARSET/UNICODE_FLAG
CHARSET / UNICODE_FLAG 默认值为0,即GB18030。 可用选值:0 【GB18030】、1【UTF-8】、2【EUC-KR】
结论:
- GB18030 => 一个汉字占用存储两个字节,一个数字或一个英文字母占用存储一个字节。
- UTF-8 => 一个汉字占用存储三个字节,一个数字或一个英文字母占用存储一个字节。
## 准备测试数据
drop table if exists t_charset;
create table t_charset(c1 varchar(20));
insert into t_charset(c1) values('一二三四五');
insert into t_charset(c1) values('12345');
insert into t_charset(c1) values('abcdef');
insert into t_charset(c1) values('一二XY67');
insert into t_charset(c1) values(',.!');
insert into t_charset(c1) values(',。!');
commit;
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
c1 ,
length(c1) 字符长度 ,
lengthb(c1) 字节长度
from
t_charset;
实验验证过程:
2.2 大小写敏感 CASE_SENSITIVE
CASE_SENSITIVE 默认值为Y/1,即大小写敏感。可用选值:Y/N or 1/0
对象标识符(表名、字段名、视图名等)和数据内容在英文字符方面是否区分大小写。结论:
- 达梦数据库大小写敏感会牵扯到含英文字符数据内容的大小写辨别和对象标识符大小写辨别。
- 当开启大小写敏感时,书写的SQL语句中涉及到对象标识符的名称,会先自动将英文字符全部转成大写,再发送到数据库服务器解析。
- 当关闭大小写敏感时,数据库解析引擎原封不动的发送而来的SQL语句中涉及的对象标示符(即:禁用自动转大大写)。
- 欲想保持书写中的对象名称原样发送到数据库服务器,可以使用英文半角双引号将其包裹。
## 准备测试数据
drop table if exists "T_CaseSensitive";
create table "T_CaseSensitive"(C1 varchar(20), "c2" varchar(20));
insert into "T_CaseSensitive"(C1, "c2") values('帅abc6', '帅ABc6');
insert into "T_CaseSensitive"(C1, "c2") values('帅ABC6', '帅abC6');
insert into "T_CaseSensitive"(C1, "c2") values('УуяЯ', 'УУЯЯ');
insert into "T_CaseSensitive"(C1, "c2") values('せか', 'セカ');
commit;
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
decode(sf_get_case_sensitive_flag(), 1, '大小写敏感', '大小写不敏感') 是否大小敏感,
c1,"c2"
from
"T_CaseSensitive";
## 测试数据内容的大小写比较
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
decode(sf_get_case_sensitive_flag(), 1, '大小写敏感', '大小写不敏感') 是否大小敏感 ,
b.c1,
case when c1="c2" then '相等'
else '不相等'
end "C1=c2?",
b."c2"
from "T_CaseSensitive" b;
实验验证过程:
2.3 varchar类型长度以字符为单位 LENGTH_IN_CHAR
LENGTH_IN_CHAR 默认值为N/0,可用取值: Y/N or 1/0
结论:
- 切记:此参数针对所有VARCHAR类型对象的长度以字符为单位生效。
- 当开启varchar类型长度是以字符为长度时,其定义长度并非真正按照字符长度调整,
而是将存储长度值按照‘理论’字符长度进行放大,会出现实际可插入字符数超过定义长度的情况。- length_in_char=1, 它存储的字节长度8188上限仍然不变,即使定义列长度为8188字符,其实际能插入的字符串占用总字节长度仍然不能超过8188,同时它也受页大小的影响。
- varchar类型自动扩充长度的规则与字符集息息相关,如下表所示:gbk2, utf84
- why? 扩充倍数原因? 考虑字符集表示,GBK最大用两个字节表示一个字符,UTF8最大用四个字节表示一个字符。
##准备测试数据
drop table if exists t_varchar;
create table t_varchar(c1 varchar(10), c2 varchar2(10), c3 char(10));
insert into t_varchar(c1,c2,c3) values('1234567890', '1234567890', '1234567890');
commit;
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
(select "value" from v$parameter where name='length_in_char') "length_in_char",
b.c1,b.c2,b.c3
from t_varchar b;
# 验证插入字符串内容超过列定义长度,同时证明varchar2和char类型
insert into t_varchar(c1,c2,c3) values('123456789012', '1234567890', '1234567890');
insert into t_varchar(c1,c2,c3) values('1234567890', '123456789012', '1234567890');
insert into t_varchar(c1,c2,c3) values('1234567890', '1234567890', '123456789012');
实验验证过程:
2.4 空白字符填充模式 BLANK_PAD_MODE
BLANK_PAD_MODE 默认值为0,可用选值:0/1
用作字符串比较,结尾空格填充模式是否兼容ORACLE。
主要作用:是否忽略字符串尾部空格参与运算。
注意:group by分组列无论blank_pad_mode取值如何,它严格处理尾部空格存在问题。
## 准备测试数据
drop table if exists t_blank;
create table t_blank(c1 varchar(10), c2 varchar(10));
insert into t_blank(c1, c2) values('abc','abc ');
insert into t_blank(c1, c2) values('abc','abc ');
insert into t_blank(c1, c2) values('abc',' abc');
insert into t_blank(c1, c2) values('abc','a bc');
insert into t_blank(c1, c2) values('abc','abc');
commit;
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
(select "value" from v$parameter where name='blank_pad_mode') "blank_pad_mode",
b.c1,b.c2
from t_blank b;
## 测试参数作用、字符串日常操作比较
select
decode(sf_get_unicode_flag(), 0, 'GB18030', 1, 'UTF-8', 'EUC-KR') 字符集,
(select "value" from v$parameter where name='blank_pad_mode') "blank_pad_mode",
b.c1,
case when b.c1=b.c2 then '等于'
else '不等于'
end "是否相等?",
b.c2,
replace(b.c2,' ','x') "填充字符"
from t_blank b;
实验验证过程:
2.5 页大小 PAGE_SIZE
PAGE_SIZE 默认值为8,可用选值:4, 8, 16, 32 单位(省写):K
数据页(又称数据块)是达梦数据库中最小的数据存储单元,也是磁盘IO读写操作的基本单元。
此参数影响数据库基表实际可存储的字符串(varchar/char/)长度,实际插入表中的列内容长度受到记录长度的限制, 每条记录的总长度不能大于数据页大小的一半(大字段列除外)。 页大小也会影响单个数据文件的最大大小和最小大小指定。
注意:一旦数据库初始化后,数据页大小无法直接随意改变,仅须重新指定新的页大小初始化库,通过DTS或dimp迁移到新初始库中。
页大小与列最大长度、记录总长度对照关系表,如下图所示:
实验验证过程:
SQL> select sf_get_page_size();
8192
### 例1:定义的字符类型超过最大长度
create table t_collen (c1 varchar(8190));
执行失败(语句1)
第1 行附近出现错误[-6121]:
数据精度超出范围
### 例2:定义的字符类型刚才最大长度
create table t_collen (c1 varchar(8188));
执行成功, 执行耗时9毫秒. 执行号:49530
影响了0条记录
### 例3:页大小8K, 插入字符串长度为定义长度.
-- 实质:插入字符已经超过页大小的一半(4095个字节),就会报错“记录超长”
declare
v_a varchar2(8188);
v_sql varchar2(8188);
begin
v_a :='';
for i in 1..8188
loop
v_a:=v_a||'a';
end loop;
v_sql:='insert into t_collen(c1) values ('''||v_a||''')';
print(v_sql);
execute immediate v_sql;
commit;
end;
/
执行失败(语句1)
记录超长
### 例4:在例3基础上开启t_collen表超长记录
alter table t_collen enable using long row;
declare
v_a varchar2(8188);
v_sql varchar2(8188);
begin
v_a :='';
for i in 1..8188
loop
v_a:=v_a||'a';
end loop;
v_sql:='insert into t_collen(c1) values ('''||v_a||''')';
print(v_sql);
execute immediate v_sql;
commit;
end;
/
select * from "SYSDBA"."t_collen";
### 例5:保证插入的字符不超过行记录总长度
drop table if exists t_collen;
create table t_collen (c1 varchar(3879));
declare
v_a varchar2(8188);
v_sql varchar2(8188);
begin
v_a :='';
for i in 1..3879
loop
v_a:=v_a||'a';
end loop;
v_sql:='insert into t_collen(c1) values ('''||v_a||''')';
print(v_sql);
execute immediate v_sql;
commit;
end;
/
3. 个人经验
- 达梦数据库的大小写敏感涉及到数据内容本身,这个最应注意问清开发方真实需求,否则在DTS迁移过程中可能会出现创建唯一性约束失败。
- 数据页的大小影响着一张基表的列数据和行数据的迁移(列长度上限和行总长度限),同时需考虑聚集索引与大字段列不能够在达梦普通表中一起出现,就算把超长记录开启和列大字段类型。
- 确定数据库的字符集尤为重要,当从异构数据库迁移过来,由于列长度定义不够,可能会出现字符串断臂情况(尾巴处乱码)。
- varchar类型以字符为单位长度,抓住重点关键字“varchar/varchar2”类型的列,即只针对varchar类型规则奏效,它会在原表基础上自动扩充2倍或者4倍(扩大倍数由字符集而定)。纵然我们查看原表定义看似定义长度,实际底层已经做转换字符单位的隐式转换。
- 空白字符填充的表现行为主要体现在末尾空格的字符串与非末尾空格的字符串的等价性比较,取决于是否严格辨别。这个坑前不久在做函数迁移时,深有体会。