oracle伪列 rowid和rownum
整理ROWID一
一,什么是伪列RowID?
1,首先是一种数据类型,唯一标识一条记录物理位置的一个id,基于64位编码的18个字符显示。
2,未存储在表中,可以从表中查询,但不支持插入,更新,删除它们的值。
二,RowID的用途
1,在开发中使用频率应该是挺多的,特别在一些update语句中使用更加频繁。所以oracle ERP中大部份的视图都会加入rowid这个字段。
?? 在一些cursor定义时也少不了加入rowid。但往往我们在开发过程中,由于连接的表很多,再加上程序的复制,有时忽略了rowid对应的是那一个表中rowid,所以有时过程出错,
?? 往往发上很多时间去查错,最后查出来既然是update时带的rowid并非此表的rowid,所以在发现很多次的错误时,重视rowid起来了,开发中一定要注意rowid的匹配
2,能以做快的方式访问表中的一行。
3,能显示表的行是如何存储的。
4,作为表中唯一标识。
三,RowID的组成
rowid确定了每条记录是在Oracle中的哪一个数据对象,数据文件、块、行上。
ROWID 的格式如下:
?? 数据对象编号??????? 文件编号??????? 块编号??????????? 行编号
?? OOOOOO???????????? FFF??????????????? BBBBBB??? RRR
??
?? 由 data_object_id# + rfile# + block# + row#?? 组成,占用10个bytes的空间,
??? 32bit的 data_object_id#,
??? 10 bit 的 rfile#,??
??? 22bit 的 block#,
??? 16 bit 的 row#.??
?? 所以每个表空间不能超过1023个 数据文件。
四,RowID的应用
1,查找和删除重复记录
?? 当试图对库表中的某一列或几列创建唯一索引时,
?? 系统提示 ORA-01452 :不能创建唯一索引,发现重复记录。
??
??? /*conn scott/tiger
??? Create table empa as select * from emp;
??? 插入重复记录
??? insert into empa select * from emp where empno = 7369;
??? insert into empa select * from emp where empno = 7839;
??? insert into empa select * from emp where empno = 7934;
??? */
?? 查找重复记录的几种方法:
??? 查找大量重复记录
??? select empno from empa group by empno having count(*) >1;
??? Select * From empa Where ROWID Not In(Select Min(ROWID) From empa Group By empno);
??? 查找少量重复记录
??? select * from empa a where rowid<>(select max(rowid) from empa where empno=a.empno );
?? 删除重复记录的几种方法:
??? (1).适用于有大量重复记录的情况(列上建有索引的时候,用以下语句效率会很高):
??? Delete empa Where empno In (Select empno From empa Group By empno Having Count(*) > 1)
??? And ROWID Not In (Select Min(ROWID) From empa Group By empno Having Count(*) > 1);
???
??? Delete empa Where ROWID Not In(Select Min(ROWID) From empa Group By empno);
???
??? (2).适用于有少量重复记录的情况(注意,对于有大量重复记录的情况,用以下语句效率会很低):
??? Delete empa a where rowid<>(select max(rowid) from empa where empno=a.empno );
?
整理ROWID二??
?????? 搞oracle都会经常碰到rowid,本文是笔者根据网上各位的文章,加上自己学习中的体会,总结而成。
一.rowid简介
??????? rowid就是唯一标志记录物理位置的一个id,在oracle 8版本以前,rowid由file#+block#+row#组成,占用6个bytes的空间,10 bit 的 file# ,22bit 的 block# ,16 bit 的 row#。
????????? 从oracle 8开始rowid变成了extend rowid,由data_object_id#+rfile#+block#+row#组成,占用10个bytes的空间, 32bit的 data_object_id#,10 bit 的 rfile#,22bit 的 block#,16 bit 的 row#.由于rowid的组成从file#变成了rfile#,所以数据文件数的限制也从整个库不能超过1023个变成了每个表空间不能超过1023个 数据文件。
??????? 说了rowid的组成,那么我们再来看看rowid在索引里面占用的字节数又是什么样子的。在oracle 8以前索引中存储的rowid占用字节数也是6bytes,在oracle8之后,虽然oracle使用了extend rowid,但是在普通索引里面依然存储了bytes的rowid,只有在global index中存储的是10bytes的extend rowid,而extend rowid也是global index出现的一个必要条件,下面我们会解释原因。
??????? 为什么golbal index需要把data_object_id#也包含在index rowid entry中呢?如果不包含会这么样?首先我们需要知道index的rowid entry的存在是为了能根据它找到表的这条记录存在哪个具体的物理位置,我们需要知道它在哪个数据文件,在哪个block,在那一行,普通的索引 oracle根据rfile#,block#,row#就可以知道了,但是partition table可以分布在多个表空间,也就是可以分布在多个数据文件,当我们建立local index时,index rowid entry并不包含data_object_id#,因为oracle可以知道这个index对应的是哪一个table分区,并可以得到table分区的 ts#(tablespace号),那么oracle根据ts#和rfile#就可以找到具体的数据文件。但是如果换成是golbal index,如果不包含data_object_id#,那么我们并不能知道这个索引对应着哪个表分区,也自然不能知道它的rfile#和file#的转 换关系,所以它将找不到所对应的记录。包含data_object_id#后,oracle可以根据data_object_id#实现rfile#和 file#的转换然后找到记录对应的物理位置。需要注意的是要理解以上概念我们还是需要了解file#和rfile#的区别。
二.比较file#和rfile#
oracle数据文件为什么存在file#和rfile#?
???????? 归根结底的原因是因为 ROWID 的存储格式造成的,因为 rowid 中文件编号标志只有10bit,最大数据容量1024,由于不存在0编号文件,所以实际上只允许1023个文件编号。在oracle8 之前的版本的数据库中,rowid是受限的,只包括 file# /block#? /row#? ,则数据库最多只允许1023个文件。
??????? 而oracle8开始rowid 包括 data_object_id# / Rfile#? /block# /rowid# 。data object id 的引入,同时支持了表分区的概念,一个表可以拥有多个分区(segment),而一个分区可以在不同的表空间中(由Rfile# 表示在segment对应的表空间中对应的 相对文件编号)。这样表的容量也增大了。 扩展的rowid使得oracle不再局限于数据文件只能有1023个的限制,而一个表可以分区,也使得表的容量不再局限于单个表空间中(1023个文件 的限制)。
??????? 当然,你或许要问,为什么oracle不调整rowid中表示 file# 的? bit数量,这个应该是由于兼容性的引起的,在 oracle7 的索引中存储的rowid就是 file# +? block# + row# ,,因为这样处理后关于索引的存储,oracle8和oracle7没有发生变化(在oracle8中一个索引(可能分区)segment肯定对应了一个 表(可能分区)的segment,这个可以由数据字典关系得到,从而确立了 索引中的rowid 对应哪个 表空间中的数据文件),在升级的时候就不用关心 索引的问题,而直接升级oracle软件以及运行相关的包,否则将会大动干戈解决索引的问题。这就是oracle实现物理文件升级的基础。
??????? 当 然,真正升级的时候,一些数据文件头的 rfile# 需要发生变化,这也是有文件的一些存储的特性决定的,为了不和oracle8的格式发生冲突,才需要修改。这个修改代价非常的小,所以oracle选择了 这个方案。详细的信息,大家可以去参考metalink相关内容,有详细的? 存储(byte 中字节位)的变化关系。
三.rowid举例
1.创建一临时表
create table test_rowid (id number, row_id rowid);
2.插入一行记录
insert into test_rowid values(1,null);
3.修改刚插入的记录
update test_rowid set row_id = rowid where id = 1;
4.查看rowid
select rowid,row_id from test_rowid;
返回结果为:
rowid????????????????????????????????????????????????????? row_id
AAAO0DAAJAAAAMYAAA?????????????? AAAO0DAAJAAAAMYAAA
Oracle的物理扩展ROWID有18位,每位采用64位编码,分别用A~Z、a~z、0~9、+、/共64个字符表示。A表示0,B表示1,……Z表示25,a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63。
ROWID具体划分可以分为4部分。
(1).OOOOOO:前6位表示DATA OBJECT NUMBER,将起转化位数字后匹配DBA_OBJECTS中的DATA_OBJECT_ID,可以确定表信息。
如上面例子中的DATA OBJECT NUMBER是AAAO0D,转化位数字是14×64×64 +52×64 + 3。
输入以下查询:
select owner, object_name from dba_objects where data_object_id = 14*64*64 + 52*64 + 3;
返回:
OWNER??? OBJECT_NAME
WG???????????? TEST_ROWID
(2)FFF:第7到9位表示相对表空间的数据文件号。
上面的例子中是AAJ,表示数据文件9。
输入以下查询:
(3).BBBBBB:第10到15位表示这条记录在数据文件中的第几个BLOCK中。
上面的例子是AAAAMY,转化位数字是12×64+24,表示这条记录在数据文件中的第792个BLOCK。
(4).RRR:最后3位表示这条记录是BLOCK中的第几条记录。
上面的例子是AAA,表示第0条记录(总是从0开始计数)。
四.参考资料
1.oracle rowid
2.Oracle基本数据类型存储格式浅析(四)——ROWID类型(一)
3.oracle数据文件为什么存在 Rfile# and file#
---------------------------------------------------------------------------
"select * from (select * from tabname order by name) where rownum<20",但这样一来,效率会低很多。
后经笔者试验,只需在order by 的字段上加主键或索引即可让oracle先按该字段排序,然后再rownum;方法不变:??? “select * from tabname where rownum<20 order by name"
取得某列中第N大的行
select column_name from
(select table_name.*,dense_rank() over (order by column desc) rank from table_name)
where rank = &N;
假如要返回前5条记录:
select * from tablename where rownum<6;(或是rownum <= 5 或是rownum != 6)
假如要返回第5-9条记录:
select * from tablename
where …
and rownum<10
minus
select * from tablename
where …
and rownum<5
order by name
选出结果后用name排序显示结果。(先选再排序)
注意:只能用以上符号(<、<=、!=)。
select * from tablename where rownum != 10;返回的是前9条记录。
不能用:>,>=,=,Between...and。由于rownum是一个总是从1开始的伪列,Oracle 认为这种条件不成立。
另外,这个方法更快:
select * from (
select rownum r,a from yourtable
where rownum <= 20
order by name )
where r > 10
这样取出第11-20条记录!(先选再排序再选)
要先排序再选则须用select嵌套:内层排序外层选。
rownum是随着结果集生成的,一旦生成,就不会变化了;同时,生成的结果是依次递加的,没有1就永远不会有2!
rownum 是在查询集合产生的过程中产生的伪列,并且如果where条件中存在 rownum 条件的话,则:
1: 假如判定条件是常量,则:
只能 rownum = 1, <= 大于1 的自然数, = 大于1 的数是没有结果的;大于一个数也是没有结果的
即 当出现一个 rownum 不满足条件的时候则 查询结束 this is stop key(一个不满足,系统将该记录过滤掉,则下一条记录的rownum还是这个,所以后面的就不再有满足记录,this is stop key);
2: 假如判定值不是常量,则:
若条件是 = var , 则只有当 var 为1 的时候才满足条件,这个时候不存在 stop key ,必须进行full scan ,对每个满足其他where条件的数据进行判定,选出一行后才能去选rownum=2的行……
?
以下摘自《中国IT实验室》
1.在oracle中实现select top n
?? 由于oracle不支持select top语句,所以在oracle中经常是用order by跟rownum的组合来实现select top n的查询。
简单地说,实现方法如下所示:
select 列名1...列名n from????
(select 列名1...列名n from 表名 order by 列名1...列名n)
where rownum<=n(抽出记录数)
order by rownum asc
?? 下面举个例子简单说明一下。
顾客表customer(id,name)有如下数据:
ID NAME
?? 01 first
?? 02 Second
?? 03 third
?? 04 forth
?? 05 fifth
?? 06 sixth
?? 07 seventh
?? 08 eighth
?? 09 ninth
?? 10 last
?? 则按NAME的字母顺抽出前三个顾客的SQL语句如下所示:
select * from
?? (select * from customer order by name)
?? where rownum<=3
?? order by rownum asc
?? 输出结果为:
?? ID NAME
?? 08 eighth
?? 05 fifth
?? 01 first