Oracle中的move命令
				
		
		
				
				 
		
				
						
								    从8i开始,oracle开始提供Move的命令。我们通常使用这个命令,将一个table segment从一个tablespace移动到另一个tablespace。Move实际上是在block之间物理的copy数据,那么,我们可以通过这种方式来降低table的HWM。我们先通过一个实验来看看move是如何移动数据的。
				
		
		
				
						
						
				 
		
				
				 
		
				
						
								1.建表并插入数据:
						
				
		
		
				
						
						
				 
		
				
						SQL> create table sjh.test1(id int) tablespace users;
				
		
		
				
						
						
				 
		
				
						表已创建。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(1);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(2);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(3);
				
		
		
				
						
								
								
						
				 
		
				
						
								
								已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(4);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(5);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(6);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(7);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						SQL> insert into sjh.test1 values(8);
				
		
		
				
						
						
				 
		
				
						已创建 1 行。
				
		
		
				
						
						
				 
		
				
						S
				
				
						QL> commit;
				
		
		
				
						
						
				 
		
				
						提交完成。
				
		
		
				
						
						
				 
		
				
						SQL> select * from sjh.test1;
				
		
		
				
						        ID
----------
         1
         2
         3
         4
         5
         6
         7
         8
				
		
		
				
						
						
				 
		
				
						已选择8行。
				
		
		
				
						
						
				 
		
				
				 
		
				
						
								2.查看表的rowid信息和block id信息:
						
				
		
		
				
						
						
				 
		
				
						SQL> select rowid,id from sjh.test1;
				
		
		
				
						
						
				 
		
				
						ROWID                      ID
------------------ ----------
AAAMlQAAEAAAABHAAA          1
AAAMlQAAEAAAABHAAB          2
AAAMlQAAEAAAABHAAC          3
AAAMlQAAEAAAABHAAD          4
AAAMlQAAEAAAABHAAE          5
AAAMlQAAEAAAABHAAF          6
AAAMlQAAEAAAABHAAG          7
AAAMlQAAEAAAABHAAH          8
				
		
		
				
						
						
				 
		
				
						已选择8行。
				
		
		
				
						
						
				 
		
				SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS
 2   from dba_extents where segment_name='TEST1';
		
		
				
				 
		
				EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS
---------- ---------- ------------ ---------- ----------
         0          4            4         65          8
		
		
				
				 
		
				--8条记录都在一个块上(AAAABH)
		
		
				
						
						
				 
		
				
						
						这里简单介绍一下ROWID的知识:ROWID 在磁盘上需要10个字节的存储空间并使用18个字符来显示它包含下列组件:
		
		
				
				 
		
				数据对象编号:每个数据对象如表或索引在创建时都分配有此编号,并且此编号在数据库中是唯一的;
		
		
				相关文件编号:此编号对于一个表空间中的每个文件是唯一的;
		
		
				块编号:表示包含此行的块在文件中的位置;
		
		
				行编号:标识块头中行目录位置的位置;
		
		
				
				 
		
				在内部数据对象编号需要32 位,相关文件编号需要10 位,块编号需要22,位行编号需要16 位,加起来总共是80 位或10 个字节,ROWID 使用以64 为基数的编码方案来显示该方案将六个位置用于数据对象,编号三个位置用于相关文件编号六个位置用于块编号三个位置用于行编号以64 为基数的编码方案使用字符A-Z a-z 0-9 + 和/共64 个字符,
		
		
				
				 
		
				如下例所示:AAAMlQ AAE AAAABH AAA
		
		
				
				 
		
				在本例中
		
		
				
				
				
						AAAMlQ    是数据对象编号
				
		
		
				
						
						AAE       是相关文件编号
		
		
				AAAABH   是块编号
		
		
				AAA      是行编号
		
		
				
				 
		
				
				 
		
				
						3.做一些DML操作,再观察ROWID有没有发生变化:
				
		
		
				
				 
		
				SQL> delete from sjh.test1 where id=1;
		
		
				
				 
		
				已删除 1 行。
		
		
				
				 
		
				SQL> delete from sjh.test1 where id=3;
		
		
				
				 
		
				已删除 1 行。
		
		
				
				 
		
				SQL> delete from sjh.test1 where id=5;
		
		
				
				 
		
				已删除 1 行。
		
		
				
				 
		
				SQL> commit;
		
		
				
				 
		
				提交完成。
		
		
				
				 
		
				SQL> select rowid,id from sjh.test1;
		
		
				
				 
		
				ROWID                      ID
------------------ ----------
AAAMlQAAEAAAABHAAB          2
AAAMlQAAEAAAABHAAD          4
AAAMlQAAEAAAABHAAF          6
AAAMlQAAEAAAABHAAG          7
AAAMlQAAEAAAABHAAH          8
		
		
				
				 
		
				--我们看到ROWID保持不变。
		
		
				
				 
		
				
				 
		
				
						4.做MOVE操作,然后观察ROWID的情况:
				
		
		
				
				 
		
				SQL> alter table sjh.test1 move;
		
		
				
				 
		
				表已更改。
		
		
				
				 
		
				SQL> select rowid,id from sjh.test1;
		
		
				
				 
		
				ROWID                      ID
------------------ ----------
AAAMlRAAEAAAABMAAA          2
AAAMlRAAEAAAABMAAB          4
AAAMlRAAEAAAABMAAC          6
AAAMlRAAEAAAABMAAD          7
AAAMlRAAEAAAABMAAE          8
		
		
				
				 
		
				SQL> select EXTENT_ID,FILE_ID,RELATIVE_FNO,BLOCK_ID,BLOCKS
 2   from dba_extents where segment_name='TEST1';
		
		
				
				 
		
				EXTENT_ID    FILE_ID RELATIVE_FNO   BLOCK_ID     BLOCKS
---------- ---------- ------------ ---------- ----------
         0          4            4         73          8
		
		
				
				 
		
				--ROWID发生变化BLOCK_ID由原来的65变为73,BLOCK的编号由原来的AAAABH变为AAAABM
		
		
				
				 
		
				
				 
		
				
						5.move对HWM的影响:
				
		
		
				
				 
		
				这里引用网友yjz0065的一个例子:
		
		
				
				 
		
				SQL> create table my_objects tablespace HWM 
		
		
				
						
								 2   as select * from all_objects;
				
		
		 
		
				SQL> delete from my_objects where rownum<10000;
		
		
				
				 
		
				9999 rows deleted
		
		 
		
				SQL> select count(*) from my_objects;
		
		 
		
				COUNT(*)
		
		
				----------
		
		
				     21015
		
		
				
				 
		
				SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE');
		
		 
		
				Total Blocks............................425
		
		
				Total Bytes.............................3481600
		
		
				Unused Blocks...........................3
		
		
				Unused Bytes............................24576
		
		
				Last Used Ext FileId....................11
		
		
				Last Used Ext BlockId...................1294
		
		
				Last Used Block.........................2
		
		
				这里HWM=425 - 3 + 1 = 423
		
		
				
						
然后对table MY_OBJECTS进行move操作:
				
						
SQL> alter table MY_OBJECTS move;
				
						
表已更改。
		
		 
		
				SQL> exec show_space(p_segname => 'MY_OBJECTS',p_owner => 'DLINGER',p_type => 'TABLE');
		
		 
		
				Total Blocks............................290
		
		
				Total Bytes.............................2375680
		
		
				Unused Blocks...........................1
		
		
				Unused Bytes............................8192
		
		
				Last Used Ext FileId....................11
		
		
				Last Used Ext BlockId...................1584
		
		
				Last Used Block.........................4
		
		 
		
				我们可以看到,table MY_OBJECTS的HWM从423移动到290,table的HWM降低了!(show_space是自定义的一个过程)。
		
		
				
				 
		
				
				 
		
				
				 
		
				
						Move的一些用法:
				
		
		 
		
				以下是alter table 中move子句的完整语法,我们介绍其中的几点:
		
		 
		
				MOVE [ONLINE] 
[segment_attributes_clause] 
[data_segment_compression]
[index_org_table_clause]
[ { LOB_storage_clause | varray_col_properties }
    [ { LOB_storage_clause | varray_col_properties } ]...
]
[parallel_clause]
		
		 
		
				
				 
		
				a. 我们可以使用move将一个table从当前的tablespace上移动到另一个tablespace上,如:
		
		 
		
				alter table t move tablespace tablespace_name;
		
		 
		
				b. 我们还可以用move来改变table已有的block的存储参数,如:
		
		 
		
				alter table t move storage (initial 30k next 50k); 
		
		 
		
				c.另外,move操作也可以用来解决table中的行迁移的问题。
		
		 
		
				 
				
						
						
								使用move的一些注意事项:
						
				
				 
				
						a. table上的index需要rebuild:
				
				 
				在前面我们讨论过,move操作后,数据的rowid发生了改变,我们知道,index是通过rowid来fetch数据行的,所以,table上的index是必须要rebuild的。
SQL> create index i_my_objects on my_objects (object_id);
				 
				Index created
				 
				SQL> alter table my_objects move;
				 
				Table altered
				 
				SQL> select index_name,status from user_indexes where index_name='I_MY_OBJECTS';
				 
				INDEX_NAME                     STATUS
				------------------------------ --------
				I_MY_OBJECTS                   UNUSABLE
				 
				从这里可以看到,当table MY_OBJECTS进行move操作后,该table 上的inedx的状态为UNUSABLE,这时,我们可以使用alter index I_MY_OBJECTS rebuild online的命令,对index I_MY_OBJECTS进行在线rebuild。
				 
				
						b. move时对table的锁定
				
				 
				当我们对table MY_OBJECTS进行move操作时,查询v$locked_objects视图可以发现,table MY_OBJECTS上加了exclusive lock:
SQL>select OBJECT_ID, SESSION_ID,ORACLE_USERNAME,LOCKED_MODE from v$locked_objects;
				 
				OBJECT_ID SESSION_ID ORACLE_USERNAME    LOCKED_MODE
				---------- ---------- ------------------ -----------
				     32471          9 DLINGER                      6
				 
				SQL> select object_id from user_objects where object_name = 'MY_OBJECTS';
				OBJECT_ID
				----------
				     32471
				 
				这就意味着,table在进行move操作时,我们只能对它进行select的操作。反过来说,当我们的一个session对table进行DML操作且没有commit时,在另一个session中是不能对这个table进行move操作的,否则oracle会返回这样的错误信息:ORA-00054: 资源正忙,要求指定 NOWAIT。
				
						
						 
				
						c. 关于move时空间使用的问题:
				
				 
				当我们使用alter table move来降低table的HWM时,有一点是需要注意的,这时,当前的tablespace中需要有1倍于table的空闲空间以供使用:
				 
				SQL> CREATE TABLESPACE TEST1
				
						 2   DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\TEST1.dbf' SIZE 5M
				
						 3   UNIFORM SIZE 128K ;
				SQL> create table my_objects tablespace test1 as select * from all_objects;
				 
				表已创建。
				SQL> select bytes/1024/1024 from user_segments where segment_name='MY_OBJECTS';
				 
				BYTES/1024/1024
				---------------
				          3.125
				 
				SQL> alter table MY_OBJECTS move;
				alter table MY_OBJECTS move
				            *
				ERROR 位于第 1 行:
				 
				ORA-01652: 无法通过16(在表空间TEST1中)扩展 temp 段
				SQL> ALTER DATABASE
				
						 2  DATAFILE 'D:\ORACLE\ORADATA\ORACLE9I\TEST1.DBF' RESIZE 7M;
				数据库已更改。
				SQL> alter table MY_OBJECTS move;
				表已更改。