前几天同事提了一个问题,比较有意思,如果一张表新增字段,在数据块上是怎么存储的?是直接“加”到数据块中,还是通过其他的形式,表示新的字段?让我们从Oracle数据块内容,看下他到底是怎么存储的。
创建测试表,插入两条数据,
代码语言:javascript复制SQL> create table testadd(id number, name varchar2(1));
Table created.
...
SQL> select * from testadd;
ID N
---------- -
1 a
2 b
我们从数据块中能看到这两条记录,
代码语言:javascript复制block_row_dump:
tab 0, row 0, @0x1f98
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 1] 61
tab 0, row 1, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
场景一
增加一个字段,不带默认值,不带非空约束,
代码语言:javascript复制SQL> alter table testadd add a1 varchar2(1);
Table altered.
此时的数据块,无任何的变化,标记表的字段,仍然是两个,
代码语言:javascript复制block_row_dump:
tab 0, row 0, @0x1f98
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 1] 61
tab 0, row 1, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
如果更新已存在的一条数据的这个新增字段,
代码语言:javascript复制SQL> update testadd set a1='a' where id=1;
1 row updated.
SQL> commit;
Commit complete.
我们看到,第一条记录的cc已经改成了3,同时多了col 2这个新增字段的列,但是第二条记录,仍是两个字段,
代码语言:javascript复制tab 0, row 0, @0x1f86
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
tab 0, row 1, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
如果新增记录,
代码语言:javascript复制SQL> insert into testadd values(3, 'c', 'c');
1 row created.
SQL> commit;
Commit complete.
我们看到数据块中的第三条新增记录,已经包含了三个字段,
代码语言:javascript复制tab 0, row 0, @0x1f86
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
tab 0, row 1, @0x1f90
tl: 8 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [ 1] 62
tab 0, row 2, @0x1f7c
tl: 10 fb: --H-FL-- lb: 0x2 cc: 3
col 0: [ 2] c1 04
col 1: [ 1] 63
col 2: [ 1] 63
说明当增加一个不带默认值,不带非空约束的字段时,只有当该字段存储值,数据块中才会为其实际存储。
场景二
增加一个字段,带默认值,不带非空约束,
代码语言:javascript复制SQL> alter table testadd add a2 varchar2(1) default 'a';
Table altered.
此时我们看到,数据块中都实际存储了这个新增字段,至于原因,同学们应该了解,新增带着默认值,不带非空约束的字段,其实会执行一个全表更新的操作,会实际为该新增字段插入数据, 具体可参考《新增字段的一点一滴技巧》,
代码语言:javascript复制tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
col 3: [ 1] 61
tab 0, row 1, @0x1f65
tl: 11 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 03
col 1: [ 1] 62
col 2: *NULL*
col 3: [ 1] 61
tab 0, row 2, @0x1f59
tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 04
col 1: [ 1] 63
col 2: [ 1] 63
col 3: [ 1] 61
P.S. 应该是存储顺序上的需求,第二个字段原先未实际存储的第三个列此时做了占位。
场景三
增加一个字段,带默认值,带非空约束,
代码语言:javascript复制SQL> alter table testadd add a3 varchar2(1) default 'a' not null;
Table altered.
新增的a3字段已经有了值,
代码语言:javascript复制SQL> select id, name, a1, a2, a3 from testadd;
ID NAME A1 A2 A3
----- ------ -- -- --
1 a a a a
2 b a a
3 c c a a
但是此时数据块,并未实际存储这个新增字段,
代码语言:javascript复制tab 0, row 0, @0x1f70
tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
col 3: [ 1] 61
tab 0, row 1, @0x1f65
tl: 11 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 03
col 1: [ 1] 62
col 2: *NULL*
col 3: [ 1] 61
tab 0, row 2, @0x1f59
tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 04
col 1: [ 1] 63
col 2: [ 1] 63
col 3: [ 1] 61
如果此时新增一条记录,
代码语言:javascript复制SQL> insert into testadd values(4, 'd', 'a', 'a', 'a');
1 row created.
SQL> commit;
Commit complete.
此时数据块中,第四条记录,已经包含了五个字段,其他记录,仍是四个字段,
代码语言:javascript复制tab 0, row 0, @0x1f70
tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
col 3: [ 1] 61
tab 0, row 1, @0x1f65
tl: 11 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 03
col 1: [ 1] 62
col 2: *NULL*
col 3: [ 1] 61
tab 0, row 2, @0x1f59
tl: 12 fb: --H-FL-- lb: 0x1 cc: 4
col 0: [ 2] c1 04
col 1: [ 1] 63
col 2: [ 1] 63
col 3: [ 1] 61
tab 0, row 3, @0x1f4b
tl: 14 fb: --H-FL-- lb: 0x2 cc: 5
col 0: [ 2] c1 05
col 1: [ 1] 64
col 2: [ 1] 61
col 3: [ 1] 61
col 4: [ 1] 61
如果更新一条已存在的记录,
代码语言:javascript复制SQL> update testadd set a3='a' where id=1;
1 row updated.
SQL> commit;
Commit complete.
可以看到,第一条记录已经包含了五个字段,其他未更新记录,仍是四个字段,说明当增加一个带默认值,带非空约束的字段时,只有当该字段存储值,数据块中才会为其实际存储,
代码语言:javascript复制tab 0, row 0, @0x1f3d
tl: 14 fb: --H-FL-- lb: 0x1 cc: 5
col 0: [ 2] c1 02
col 1: [ 1] 61
col 2: [ 1] 61
col 3: [ 1] 61
col 4: [ 1] 61
tab 0, row 1, @0x1f65
tl: 11 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [ 2] c1 03
col 1: [ 1] 62
col 2: *NULL*
col 3: [ 1] 61
tab 0, row 2, @0x1f59
tl: 12 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [ 2] c1 04
col 1: [ 1] 63
col 2: [ 1] 63
col 3: [ 1] 61
tab 0, row 3, @0x1f4b
tl: 14 fb: --H-FL-- lb: 0x0 cc: 5
col 0: [ 2] c1 05
col 1: [ 1] 64
col 2: [ 1] 61
col 3: [ 1] 61
col 4: [ 1] 61
根据以上三个场景的测试,新增字段是否存在于数据块中,取决于几个条件,
- 新增字段带默认值的情况下,是否设置了非空约束。
- 该字段是否包含了值(包含让default设置的)。
- 该字段即使为空,但是在他之后,新增了其他包含值的字段,则该字段会在数据块中显示为*NULL*的占位。
无论什么问题,实践是检验真理的唯一标准。