1.认识ROWID
SQL> select rowid from dept where rownum < 2;
ROWID各列信息格式如下:
数据对象编号文件编号 块编号 行编号
OOOOOO FFF BBBBBB RRR
简单查看ROWID信息
select rowid ,
substr(rowid,1,6) "OBJECT",
substr(rowid,7,3) "FILE",
substr(rowid,10,6) "BLOCK",
substr(rowid,16,3) "ROW"
from dept
/
ROWNUM是oracle系统顺序分配为从查询返回的行的编号。
返回的第一行分配的是1,第二行是2,依此类推。
这个伪字段可以用于限制查询返回的总行数,且rownum不能以任何表的名称作为前缀。
ROWID分两种:扩展ROWID和受限制的ROWID,后者为8I时的ROWID,现在已经废弃。ROWID格式如下:
execute dbms_stats.gather_table_stats('TEST','TT1'); //分析表
SET UNUSED COLUMN会对一列进行重命名,可以在下面数据字典中看到
SQL> alter table DBA_SEGMENTS DROP unused columns; // 在这里释放空间
再次查询
rowid
其实表里的每一条数据都隐藏着一个非常重要的东西——rowid
rowid叫伪列,它并不是我们在创建表的时候所指定的具体的某一列。而是ORACLE数据库自动为每行数据所附加的一列。
比如表里面有完全相同的两行数据,我们要删除其中一行,你总得找出两行不一样的地方,ROWID就是一种选择。如下:
SQL> select A.ROWID,A.* from tt A;
ROWID ID NAME
------------------ ---------- ----------
AAANMfAAEAAAE0eAAA 1 a
AAANMfAAEAAAE0eAAB 1 a
SQL>
oracle可以根据ROWID准确地找到这行数据所存放的具体的物理位置。
ROWID的格式
SQL> select rowid ,
2 substr(rowid,1,6) "OBJECT",
3 substr(rowid,7,3) "FILE",
4 substr(rowid,10,6) "BLOCK",
5 substr(rowid,16,3) "ROW"
6 from dept
7 /
ROWID OBJECT FILE BLOCK ROW
------------------ ------------------------ ------------ ------------------------ ------------
AAAMfNAAEAAAAAQAAA AAAMfN AAE AAAAAQ AAA
AAAMfNAAEAAAAAQAAB AAAMfN AAE AAAAAQ AAB
AAAMfNAAEAAAAAQAAC AAAMfN AAE AAAAAQ AAC
AAAMfNAAEAAAAAQAAD AAAMfN AAE AAAAAQ AAD
SQL>
2.数据对象编号
有两个对象编号:OBJECT_ID 和 DATA_OBJECT_ID,前者是指对象编号,后者是数据对象编号,也就是段编号,也就是ROWID的前6个字符标识。
对象和段是两个概念,是对象不一定是段,是段肯定就是对象。
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
-------------------------------------------------- ---------- --------------
TT 54047 54047
SQL>
比如你在数据库中建立了一个存储过程,它在数据库中就是一个对象。表、视图、函数、存储过程在数据库中都叫对象。
我们经常提到的段其实就是表和索引。ORACLE里有数据段,索引段,临时段和回滚段。我们介绍的和数据有关的段一种是表一种是索引。当然表还有索引组织表,分区表,临时表等其他类型的表。
OBJECT_ID 和 DATA_OBJECT_ID 有什么区别呢?
这两个编号在刚刚创建的时候都是一样的。
OBJECT_ID:对象编号。凡是对象,数据库都会给它分配一个对象编号,对象一旦建立,对象编号就不会再次发生改变。即使对象名称变了,对象编号也不变。如下:
SQL> conn scott/tiger;
SQL> alter table tt rename to tt1;
SQL> conn / as sysdba
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT1';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
-------------------------------------------------- ---------- --------------
TT1 54047 54047
SQL>
DATA_OBJECT_ID:数据段ID,其实就是段的ID,段是数据库逻辑结构的内容,它描述的是ORACLE物理空间的使用。只要它的存放位置发生了改变,DATA_OBJECT_ID 就会发生改变。如下:
表在没有迁移到另一表空间的ROWID如下:
SQL> select A.*,rowid from tt1 A;
ID NAME ROWID
---------- ---------- ------------------
1 a AAANMfAAEAAAE0eAAA
1 a AAANMfAAEAAAE0eAAB
SQL>
SQL> alter table scott.tt1 move tablespace example;
Table altered.
SQL> conn scott/tiger;
Connected.
SQL> select A.*,rowid from tt1 A;
ID NAME ROWID
---------- ---------- ------------------
1 a AAANMgAAFAAAAP0AAA
1 a AAANMgAAFAAAAP0AAB
SQL> conn / as sysdba
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT1';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
-------------------------------------------------- ---------- --------------
TT1 54047 54048
SQL>
哪些操作可以使DATA_OBJECT_ID发生变化呢?其实TRUNCATE也能使DATA_OBJECT_ID发生变化。如下:
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT1';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
-------------------------------------------------- ---------- --------------
TT1 54047 54048
SQL> truncate table scott.tt1;
Table truncated.
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where object_name='TT1';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID
-------------------------------------------------- ---------- --------------
TT1 54047 54049
SQL>
TRUNCATE 是一个DDL命令,所以TRUNCATE一张表所花的时间和表里数据的多与少无关。
TRUNCATE的原理:是DDL命令,凡是DDL命令,不涉及修改数据,只涉及修改元数据。元数据存放在数据字典中,用于描述对象的结构,对象的创建语法就是元数据。TRUNCATE表只修改SYSTEM 表空间对该表的描述,
TRUNCATE一张表会将该表重命名并重新建立一个对象。所以它的速度很快。
删除一列:要释放这列所占用的空间并修改元数据。如果列的数据大,DROP 一列可能花的时间很长,可能列上还有锁,所以删除一列风险很大
SYS用户下的列不支持删除操作,如下所示:
SQL> conn / as sysdba
Connected.
SQL> create table t1 (id int,name varchar2(10));
Table created.
SQL> alter table t1 drop column id;
alter table t1 drop column id
*
ERROR at line 1:
ORA-12988: cannot drop column from table owned by SYS
SQL>
SQL> alter table dba_segments drop column SEGMENT_NAME; // 删除列后数据再也无法找回
Table altered.
SQL>
SQL> exec dbms_stats.gather_table_stats('SCOTT','DBA_SEGMENTS');
PL/SQL procedure successfully completed.
SQL> select BLOCKS,EMPTY_BLOCKS from user_tables where table_name='DBA_SEGMENTS';
BLOCKS EMPTY_BLOCKS
---------- ------------
73 7
SQL>
直接使某一列无效
SQL> alter table DBA_SEGMENTS set unused column HEADER_BLOCK; // 不释放空间,会重命名列。删除列后数据再也无法找回,它不释放空间,在数据库繁忙的时候可用此方法
Table altered.
SQL>
SQL> select OWNER,TABLE_NAME,COLUMN_NAME,DATA_TYPE from dba_tab_cols where table_name='DBA_SEGMENTS';
OWNER TABLE_NAME COLUMN_NAME DATA_TYPE
---------- -------------------- ------------------------------ --------------------
SCOTT DBA_SEGMENTS OWNER VARCHAR2
SCOTT DBA_SEGMENTS SEGMENT_TYPE VARCHAR2
SCOTT DBA_SEGMENTS HEADER_FILE NUMBER
SCOTT DBA_SEGMENTS BYTES NUMBER
SCOTT DBA_SEGMENTS BLOCKS NUMBER
SCOTT DBA_SEGMENTS EXTENTS NUMBER
SCOTT DBA_SEGMENTS INITIAL_EXTENT NUMBER
SCOTT DBA_SEGMENTS NEXT_EXTENT NUMBER
SCOTT DBA_SEGMENTS MIN_EXTENTS NUMBER
SCOTT DBA_SEGMENTS MAX_EXTENTS NUMBER
SCOTT DBA_SEGMENTS PCT_INCREASE NUMBER
OWNER TABLE_NAME COLUMN_NAME DATA_TYPE
---------- -------------------- ------------------------------ --------------------
SCOTT DBA_SEGMENTS FREELISTS NUMBER
SCOTT DBA_SEGMENTS FREELIST_GROUPS NUMBER
SCOTT DBA_SEGMENTS RELATIVE_FNO NUMBER
SCOTT DBA_SEGMENTS BUFFER_POOL VARCHAR2
SYS DBA_SEGMENTS INITIAL_EXTENT NUMBER
SYS DBA_SEGMENTS NEXT_EXTENT NUMBER
SYS DBA_SEGMENTS PCT_INCREASE NUMBER
SYS DBA_SEGMENTS BUFFER_POOL VARCHAR2
SYS DBA_SEGMENTS OWNER VARCHAR2
SYS DBA_SEGMENTS BLOCKS NUMBER
SYS DBA_SEGMENTS HEADER_FILE NUMBER
OWNER TABLE_NAME COLUMN_NAME DATA_TYPE
---------- -------------------- ------------------------------ --------------------
SYS DBA_SEGMENTS MAX_EXTENTS NUMBER
SCOTT DBA_SEGMENTS SYS_C00004_12082923:33:29$ NUMBER
SYS DBA_SEGMENTS RELATIVE_FNO NUMBER
SYS DBA_SEGMENTS TABLESPACE_NAME VARCHAR2
SYS DBA_SEGMENTS FREELIST_GROUPS NUMBER
SYS DBA_SEGMENTS EXTENTS NUMBER
SYS DBA_SEGMENTS PARTITION_NAME VARCHAR2
SYS DBA_SEGMENTS BYTES NUMBER
SYS DBA_SEGMENTS HEADER_BLOCK NUMBER
SYS DBA_SEGMENTS FREELISTS NUMBER
SYS DBA_SEGMENTS SEGMENT_NAME VARCHAR2
OWNER TABLE_NAME COLUMN_NAME DATA_TYPE
---------- -------------------- ------------------------------ --------------------
SYS DBA_SEGMENTS MIN_EXTENTS NUMBER
SYS DBA_SEGMENTS SEGMENT_TYPE VARCHAR2
35 rows selected.
SQL>
SQL> alter table DBA_SEGMENTS DROP unused columns; // 在这里释放空间
3.表空间脱机不能访问表里的数据却能删除表
表空间脱机后,里面的数据不能访问,但里面的对象却可以删除,因为删除对象是DDL命令,DDL命令只涉及修改元数据,而元数据在SYSTEM表空间里,真正的数据才存放在USERS表空间里。
SQL> alter tablespace users offline;
Tablespace altered.
SQL> select * from scott.emp;
select * from scott.emp
*
ERROR at line 1:
ORA-00376: file 4 cannot be read at this time
ORA-01110: data file 4: '/u01/app/oracle/oradata/caland/users01.dbf'
SQL> drop table scott.emp;
Table dropped.
SQL>
SQL> flashback table emp to before drop;
flashback table emp to before drop
*
ERROR at line 1:
ORA-38305: object not in RECYCLE BIN
SQL> conn scott/tiger;
Connected.
SQL> flashback table emp to before drop;
Flashback complete.
SQL>
4.相对文件编号
数据文件编号有相对文件编号和绝对文件编号
SQL> conn / as sysdba
Connected.
SQL> select file_id,relative_fno from dba_data_files;
FILE_ID RELATIVE_FNO
---------- ------------
4 4
3 3
2 2
1 1
5 5
7 7
6 6
8 8
8 rows selected.
SQL>
针对8K的数据文件,每个数据库有65535个数据文件,每个表空间最多有1023个数据文件。当一个数据库里的数据文件数小于等于1023的时候,相对文件编号和绝对文件编号相同。
绝对文件编号是针对整个数据库而言的,相对文件编号是相对某个表空间而言的,但为什么相对文件编号和绝对文件编号还会相同呢?有待研究。
5.ROWID的编码格式
ROWID是一个由64进制的组成的长度为16的字符串。
AAANMf AAE AAAE0e AAA (6+3+6+3=16个字符)
A~Z表示0~25
a~z表示26~51
0~9表示52~61
+表示62
/表示63
AAANMF=13*64*64+12*64+5
53248+768 +5
SQL> SELECT ROWID FROM SCOTT.EMP;
ROWID
------------------
AAAMfPAAEAAAAAgAAB
SQL>
SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,owner from dba_objects where object_name='EMP' and OWNER='SCOTT';
OBJECT_NAME OBJECT_ID DATA_OBJECT_ID OWNER
-------------------- ---------- -------------- ----------
EMP 51151 51151 SCOTT
SQL>
AAAMfP=12*64*64+31*64+15=51151
Select 12*64*64+31*64+15 from dual;
POWER(64,2)幂的用法
SQL> select 12*power(64,2) + 31*64+15 from dual;
12*POWER(64,2)+31*64+15
-----------------------
51151
SQL>
6.利用包得到ROWID信息
select
rowid,
dbms_rowid.rowid_object(rowid) obj,
dbms_rowid.rowid_relative_fno(rowid) fno,
dbms_rowid.rowid_block_number(rowid) bno,
dbms_rowid.rowid_row_number(rowid) rno
from emp
where rownum < 6;
ROWID OBJ FNO BNO RNO
------------------ ---------- ---------- ---------- ----------
AAAM1jAABAAAO2SAAA 52579 1 60818 0
AAAM1jAABAAAO2SAAB 52579 1 60818 1
AAAM1jAABAAAO2SAAC 52579 1 60818 2
AAAM1jAABAAAO2SAAD 52579 1 60818 3
AAAM1jAABAAAO2SAAE 52579 1 60818 4
SQL>
7.根据ROWID四部分得到ROWID
SQL> select dbms_rowid.rowid_create(1,52579,1,60818,0) from dual; //第一个参数有两个值,0表示受限制的ROWID,1表示扩展ROWID
DBMS_ROWID.ROWID_C
------------------
AAAM1jAABAAAO2SAAA
SQL>