我们都知道如果想修改分区表的分区键的值如果跨越了分区,那么必须加入ENABLE ROW MOVEMENT 进行,因为此时可能的ROWID会出现变动,
关于ROWID 如下:
Object ID (4 bytes) + DBA (4 bytes) + Row (2 bytes)
其中DBA包含了BLOCK地址和DATAFILE地址,如果UPDATE分区键的记录,可能的DATAFILE和BLOCK 都需要变动,所以要开启ENABLE ROW MOVEMENT。
而修改分区索引的键值在多个分区中移动为什么没有问题呢?如下:
SQL> CREATE INDEX month_ix ON testpar1(ic)
2 GLOBAL PARTITION BY RANGE(ic)
3 (PARTITION pm1_ix VALUES LESS THAN (1000),
4 PARTITION pm2_ix VALUES LESS THAN (2000),
5 PARTITION pm3_ix VALUES LESS THAN (3000),
6 PARTITION pm4_ix VALUES LESS THAN (4000),
7 PARTITION pm5_ix VALUES LESS THAN (5000),
8 PARTITION pm6_ix VALUES LESS THAN (6000),
9 PARTITION pm7_ix VALUES LESS THAN (7000),
10 PARTITION pm8_ix VALUES LESS THAN (8000),
11 PARTITION pm12_ix VALUES LESS THAN (MAXVALUE));
建立一个范围分区索引,然后我们进行UPDATE跨分区的,注意这里使用GLOBAL分区,这样我们我们用于测试修改非表分区字段,而是分区索引字段的值进行变动分区
SQL> update testpar1 set ic=10040 where ic=999;
1 row updated
此行必然造成分区间的移动数据,但是是没有问题的,如下我们通过DUMP进行仔细的分析一次
进行索引块截取一部分
row#0[8020] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 02
col 1; len 6; (6): 02 00 0d f3 00 00
row#1[8008] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 03
col 1; len 6; (6): 02 00 0d f3 00 01
row#2[7996] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 04
col 1; len 6; (6): 02 00 0d f3 00 02
可以看到COL 1就是ROWID,这里的ROWID使用的是SHORT模式如下:
DBA (4 bytes) + Row (2 bytes)
这里我们取值
col 0; len 2; (2): c1 03
col 1; len 6; (6): 02 00 0d f3 00 01
进行COL0换算
SQL> select to_number('c1','xxxxxx') from dual;
TO_NUMBER('C1','XXXXXX')
------------------------
193
SQL> select to_number('03','xxxxxx') from dual;
TO_NUMBER('03','XXXXXX')
------------------------
3
指数 193-193=0
数字1 (3-1)*100^0=2
所以这里COL0的数据位实际是2
而ROWID是02 00 0d f3 00 01,进行换算
SQL> select to_number('02000df3','xxxxxxxxx') from dual;
TO_NUMBER('02000DF3','XXXXXXXX
------------------------------
33558003
SQL> SELECT dbms_utility.data_block_address_block(33558003) "BLOCK",
2 dbms_utility.data_block_address_file(33558003) "FILE"
3 FROM dual;
BLOCK FILE
---------- ----------
3571 8
而0001就是第二行而已,实际这行的ROWID对应了FILE 8 BLOCK 3571的第二行
然后我们取出表中原始记录的ROWID
SQL> select dump(rowid,16) from testpar1 where ic=2;
DUMP(ROWID,16)
--------------------------------------------------------------------------------
Typ=69 Len=10: 0,1,c0,80,2,0,d,f3,0,1
这个结果和INDEX中的ROWID
col 1; len 6; (6): 02 00 0d f3 00 01
完全一致,然后我们进行对原表记录进行更改进行跨索引跨分区测试
SQL> update testpar1 set ic=30000 where ic=2;
1 row updated
SQL> commit;
Commit complete
这样实际上我们索引记录已经由分区pm1_ix移动到MAXVALUE分区pm12_ix,这种情况下我们再来看源行的ROWID
SQL> select dump(rowid,16) from testpar1 where ic=30000;
DUMP(ROWID,16)
--------------------------------------------------------------------------------
Typ=69 Len=10: 0,1,c0,80,2,0,d,f3,0,1
可以看到ROWID并没有改变,这是一定的。
然后我们在索引中找到这个块,但是如何中得到呢?由于索引是排序好的结构30000是其中最大的数据,找到这个30000的值我们需要找到最后一个有数据的索引块
首先DUMP这个pm12_ix的 BMB LEVEL 3块也就是段头块关注如下数据:
--------------------------------------------------------
Segment Type: 2 nl2: 1 blksz: 8192 fbsz: 0
L2 Array start offset: 0x00001434
First Level 3 BMB: 0x00000000
L2 Hint for inserts: 0x02404cc9
Last Level 1 BMB: 0x02404cc8
Last Level II BMB: 0x02404cc9
Last Level III BMB: 0x00000000
Map Header:: next 0x00000000 #extents: 2 obj#: 114826 flag: 0x10000000
Inc # 0
Extent Map
-----------------------------------------------------------------
0x02404cc8 length: 8
0x02404cd0 length: 8
Auxillary Map
--------------------------------------------------------
Extent 0 : L1 dba: 0x02404cc8 Data dba: 0x02404ccb
Extent 1 : L1 dba: 0x02404cc8 Data dba: 0x02404cd0
--------------------------------------------------------
可以清楚的看到EXTENT 0和1实际由一个L1块进行管理0x02404cc8,然后我们DUMP出这个L1块
关注如下信息:
--------------------------------------------------------
DBA Ranges :
--------------------------------------------------------
0x02404cc8 Length: 8 Offset: 0
0x02404cd0 Length: 8 Offset: 8
0:Metadata 1:Metadata 2:Metadata 3:25-50% free
4:FULL 5:FULL 6:FULL 7:FULL
8:FULL 9:unformatted 10:unformatted 11:unformatted
12:unformatted 13:unformatted 14:unformatted 15:unformatted
可以看到这里实际到了第九个块8:FULL,注意这里0:Metadata 1:Metadata 2:Metadata 实际上是
BMB 1,BMB 2,段头和BMB 3原数据块。
这样实际我们进行0x02404cd0的DUMP就可以找到数据了。
SQL> select to_number('02404cd0','xxxxxxxxx') from dual;
TO_NUMBER('02404CD0','XXXXXXXX
------------------------------
37768400
SQL>
BLOCK FILE
---------- ----------
19664 9
进行dump发现数据就在最后如下:
row#84[6928] flag: ------, lock: 0, len=12
col 0; len 2; (2): c3 02
col 1; len 6; (6): 02 40 4c 83 01 e1
row#85[6914] flag: ------, lock: 0, len=14
col 0; len 4; (4): c3 02 01 29
col 1; len 6; (6): 02 00 0d f5 01 a8
row#86[6902] flag: ------, lock: 2, len=12
col 0; len 2; (2): c3 04
col 1; len 6; (6): 02 00 0d f3 00 01
----- end of leaf block dump -----
End dump data blocks tsn: 4 file#: 9 minblk 19664 maxblk 19664
这里我们找到了
row#86[6902] flag: ------, lock: 2, len=12
col 0; len 2; (2): c3 04
col 1; len 6; (6): 02 00 0d f3 00 01
进行换算COL 0
指数 195-193=2
数字1 (4-1)*100^2=30000
可以看出没有问题就是我们修改的哪一行在关注ROWID
col 1; len 6; (6): 02 00 0d f3 00 01
和
SQL> select dump(rowid,16) from testpar1 where ic=30000;
DUMP(ROWID,16)
--------------------------------------------------------------------------------
Typ=69 Len=10: 0,1,c0,80,2,0,d,f3,0,1
对比没有问题他确实没有变化,这样确切的说明了更改GLOBAL PARTITION INDEX的分区键的值不造成任何问题的原因
在于原表原数据的ROWID没有发生,而在INDEX PARATION中移动是不涉及到ROWID的。所以没有问题