前一阵看完文档对oracle的基本数据类型的存储格式有了一些了解最近有做了一些测试进行了验证
打算整理总结一下这一篇主要说明字符类型的存储格式主要包括charvarchar和long等几种类型
SQL> create table test_char (char_col char() varchar_col varchar() long_col long);
表已创建
SQL> insert into test_char values (abc fd);
已创建 行
SQL> commit;
提交完成
SQL> select rowid from test_char;
ROWID
AAABLAAFAAAAAgAAA
根据rowid的定义规则第~位是表示的是数据文件F表示而~位表示的是在这个数据文件中的第几个BLOCKg表示(rowid编码相当于进制用A~Z a~z ~ + /共个字符表示A表示B表示……a表示……表示……+表示/表示)
我们根据计算的结果去dump这个block
SQL> ALTER SYSTEM DUMP DATAFILE BLOCK ;
系统已更改
打开产生的trace文件
data_block_dumpdata header at x
===============
tsiz: xf
hsiz: x
pbl: x
bdba: x
flag=
ntab=
nrow=
frre=
fsbo=x
fseo=xf
avsp=xfe
tosp=xfe
xe:pti[] nrow= offs=
x:pri[] offs=xf
block_row_dump:
tab row @xf
tl: fb: HFL lb: x cc:
col : []
col : [ ]
col : [ ] c
end_of_block_dump
End dump data blocks tsn: file#: minblk maxblk
观察dump出来的结果可以发现以下几点
对于每个字段除了保存字段的值以外还会保存当前字段中数据的长度而且oracle显然没有把字段的长度定义或类型定义保存在block中这些信息保存在oracle的数据字典里面
根据dump的结果可以清楚的看到字符类型在数据库中是以ascii格式存储的
SQL> select chr(to_number( xx)) from dual;
CH
a
char类型为定长格式存储的时候会在字符串后面填补空格而varchar和long类型都是变长的
SQL> SELECT DUMP(CHAR_COL ) D_CHAR FROM TEST_CHAR;
D_CHAR
Typ= Len=:
SQL> SELECT DUMP(VARCHAR_COL ) D_VARCHAR FROM TEST_CHAR;
D_VARCHAR
Typ= Len=:
SQL> SELECT DUMP(LONG_COL ) D_VARCHAR FROM TEST_CHAR;
SELECT DUMP(LONG_COL ) D_VARCHAR FROM TEST_CHAR
*
ERROR 位于第 行:
ORA: 非法使用 LONG 数据类型
由于DUMP不支持LONG类型因此我们使用了alter system dump block的方式通过比较两种方式得到的结果发现DUMP()函数不但方便结果清晰而且指出了进行DUMP的数据类型在以后的例子中除非必要的情况否则都会采用DUMP()函数的方式进行说明
下面看一下插入中文的情况首先看一下数据库的字符集
SQL> select name value$ from sysprops$ where name like %CHARACTERSET%;
NAME VALUE$
NLS_CHARACTERSET ZHSGBK
NLS_NCHAR_CHARACTERSET ALUTF
SQL> insert into test_char values (定长 变长 null);
已创建 行
SQL> SELECT DUMP(CHAR_COL ) D_CHAR FROM TEST_CHAR;
D_CHAR
Typ= Len=:
Typ= Len=: baba
SQL> SELECT DUMP(VARCHAR_COL ) D_VARCHAR FROM TEST_CHAR;
D_VARCHAR
Typ= Len=:
Typ= Len=: beba
根据dump结果可以清楚的看出普通英文字符和标点用一个字节表示而中文字符或中文标点需要两个字节来表示
下面对比一下nchar和nvarchar与charvarchar类型有什么不同
SQL> create table test_nchar (nchar_col nchar() nvarchar_col nvarchar());
表已创建
SQL> insert into test_nchar values (nchar定长 nvarchar变长);
已创建 行
从这里已经可以看出一些不同了如果按照刚才中文的计算方法nvarchar变长的长度是+*=已经超过了数据类型定义的大小可是为什么插入成功了?
还是dump一下看看结果吧
SQL> select dump(nchar_col ) from test_nchar;
DUMP(NCHAR_COL)
Typ= Len=: ebaf
SQL> select dump(nvarchar_col ) from test_nchar;
DUMP(NVARCHAR_COL)
Typ= Len=: edf
这下就明白了虽然仍然是采用ascii码存储但是nchar使用的ALUTF字符集编码长度变为个字节这样中文使用两个字节对于可以用一个字节就表示的英文字符采用了高位补的方式凑足位这样对于采用ALUTF字符集的nchar类型无论中文还是英文都用位字符表示因此nvarchar变长的长度是并没有超过数据类型的限制