新增字段在数据块中的体现

2021-09-06 15:57:10 浏览数 (1)

前几天同事提了一个问题,比较有意思,如果一张表新增字段,在数据块上是怎么存储的?是直接“加”到数据块中,还是通过其他的形式,表示新的字段?让我们从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

根据以上三个场景的测试,新增字段是否存在于数据块中,取决于几个条件,

  1. 新增字段带默认值的情况下,是否设置了非空约束。
  2. 该字段是否包含了值(包含让default设置的)。
  3. 该字段即使为空,但是在他之后,新增了其他包含值的字段,则该字段会在数据块中显示为*NULL*的占位。

无论什么问题,实践是检验真理的唯一标准。

0 人点赞