欢迎投稿

今日深度:

使用rowid和rownum获取记录时要注意的问题,rowid

使用rowid和rownum获取记录时要注意的问题,rowidrownum


我们知道,rowid和rownum在Oracle中都是可以被当做伪劣使用的,主要用来定位表中特定的记录,但它们是有区别的,rowid是和行记录的物理地址对应的,而rownum则不是,是通过返回的记录集来判断首条记录,即rownum=1的记录,然后再去fetch之后的记录,rownum=2、rownum=3……,以此类推。下面来看实验:
SQL> conn zlm/zlm
Connected. SQL> create table test1 as select object_id,object_name from dba_objects where rownum<11;
Table created.
注意,创建表的时候,也用到了rownum<11这个伪列上的where条件,获取到10条记录插入到test1表。
SQL> set lin 130 pages 130 SQL> col object_name for a30 SQL> select * from test1;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#          3 I_OBJ#         25 PROXY_ROLE_DATA$         39 I_IND1         51 I_CDEF2         26 I_PROXY_ROLE_DATA$_1
10 rows selected.

用*来表示全部列,此时并不会显示出rowid,rownum这2个伪列,这也就是“伪列”这个名词的由来,是假的,fake的,我们可以使用它,但并不作为数据存储在表中
SQL> select rowid,rownum,object_id,object_name from test1;
ROWID                  ROWNUM  OBJECT_ID OBJECT_NAME ------------------ ---------- ---------- ------------------------------ AAAM+rAAGAAAACUAAA          1         20 ICOL$ AAAM+rAAGAAAACUAAB          2         44 I_USER1 AAAM+rAAGAAAACUAAC          3         28 CON$ AAAM+rAAGAAAACUAAD          4         15 UNDO$ AAAM+rAAGAAAACUAAE          5         29 C_COBJ# AAAM+rAAGAAAACUAAF          6          3 I_OBJ# AAAM+rAAGAAAACUAAG          7         25 PROXY_ROLE_DATA$ AAAM+rAAGAAAACUAAH          8         39 I_IND1 AAAM+rAAGAAAACUAAI          9         51 I_CDEF2 AAAM+rAAGAAAACUAAJ         10         26 I_PROXY_ROLE_DATA$_1
10 rows selected.
把列名全部显示指定,可以看到,rowid和rownum这两列的内容也都显示了。那我们能不能用rowid列来作为查找条件呢?当然是可以的,但前提是你要知道rowid值是怎么分布的
SQL> select rowid,object_id,object_name from test1 where rowid<to_char('AAAM+rAAGAAAACUAAK');
ROWID               OBJECT_ID OBJECT_NAME ------------------ ---------- ------------------------------ AAAM+rAAGAAAACUAAA         20 ICOL$ AAAM+rAAGAAAACUAAB         44 I_USER1 AAAM+rAAGAAAACUAAC         28 CON$ AAAM+rAAGAAAACUAAD         15 UNDO$ AAAM+rAAGAAAACUAAE         29 C_COBJ# AAAM+rAAGAAAACUAAF          3 I_OBJ# AAAM+rAAGAAAACUAAG         25 PROXY_ROLE_DATA$ AAAM+rAAGAAAACUAAH         39 I_IND1 AAAM+rAAGAAAACUAAI         51 I_CDEF2 AAAM+rAAGAAAACUAAJ         26 I_PROXY_ROLE_DATA$_1
10 rows selected.
为什么条件是<to_char('AAAM+rAAGAAAACUAAK')?因为通过刚才的观察,我们知道test1表中的第10条记录是到J,那么要获取这10条记录,就是比AAK小的这些记录
这里来说明一下rowid中这些字母表示的含义:
在Oracle 8以下,rowid(也叫受限rowid)为:FFFF.BBBBBBBB.RRRR,占用6个字节(10bit file#+22bit+16bit),但是,为了扩充的需要,如数据文件的扩充,现在的rowid改为:OOOOOOFFFBBBBBBRRR,占用10个字节(32bit+10bit rfile#+22bit+16bit)。其中,O是Object号,F是File号,B是Block号,R是Row号。由于rowid的组成从file#变成了rfile#,所以数据文件数的限制也从整个库不能超过1023个变成了每个表空间不能超过1023个数据文件这里的object_id,是与段物理存储位置相关的一个信息,因为一个段对象只可能在一个表空间上,object_id能唯一确认ts#,而object_id + rfile#就能最终定位到该rowid在哪个确定的物理数据文件上

因此,这里的AAG就表示这是第6个数据文件,要注意的是,AAA从0开始计数,行号也是如此。因此,AAA-AAJ就表示是test1表中的1-10行记录(0->9)

SQL> col name for a45 SQL> select file#,name from v$datafile;
     FILE# NAME ---------- ---------------------------------------------          1 /u01/app/oracle/oradata/ora10g/system01.dbf          2 /u01/app/oracle/oradata/ora10g/undotbs01.dbf          3 /u01/app/oracle/oradata/ora10g/sysaux01.dbf          4 /u01/app/oracle/oradata/ora10g/users01.dbf          5 /u01/app/oracle/oradata/ora10g/example01.dbf          6 /u01/app/oracle/oradata/ora10g/zlm01.dbf
zlm用户默认的表空间就是zlm,其对应的数据文件就是zlm01.dbf,可以看到,file#为6而并不是7
我们还可以用Oracle提供的dbms.rowid包来根据rowid的值来获取object_id#,rfile#,block#,row#这些具体的值:
SQL> select dbms_rowid.rowid_object('AAAM+rAAGAAAACUAAJ') object_id#,dbms_rowid.rowid_relative_fno('AAAM+rAAGAAAACUAAJ') rfile#,dbms_rowid.rowid_block_number('AAAM+rAAGAAAACUAAJ') block#,dbms_rowid.rowid_row_number('AAAM+rAAGAAAACUAAJ') row# from dual;
OBJECT_ID#     RFILE#     BLOCK#       ROW# --------------- ---------- ---------- ----------           53163          6        148          9
test1表中第10行记录就是第53163个对象、第6号文件、第148个块的第10条记录(0->9)
如果我们要获取test1表中的前5条记录,那么可以用如下的方法:
SQL> select object_id,object_name from test1 where rownum<=5;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#
或者

SQL> select object_id,object_name from test1 where rownum!=6;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#
对于第一种写法,大家都可以理解,那么为什么rownum!=6这个条件,返回的也是前5条记录呢?是不是觉得不可思议,7-10条记录也满足!=6啊,为什么不会显示呢?
来看一下官方关于rowid机制的解释:

1 Oracle executes your query.

2 Oracle fetches the first row and calls it row number 1.

3 Have we gotten past row number meets the criteria? If no, then Oracle discards the row, If yes, then Oracle return the row.

4 Oracle fetches the next row and advances the row number (to 2, and then to 3, and then to 4, and so forth).

5 Go to step 3.


当你使用rownum作为查询条件是,Oracle的SQL引擎总是会先去找row number 1这条记录,如果没有找到,那么就直接丢弃这些row,直到找到为止,然后才会有row number 2,row number 3……不断地循环这个过程,直到结束不符合条件为止
在第2个查询中,由于rownum=1符合了!=6这个条件,那么会依次fetch下去,直到取到了rownum=5,都是符合的记录,然后就会返回一个结果,而由于中间断档了,那么当找到第6条记录的时候,显然不符合!=6这个条件,那么刚才的一轮循环就结束了,而之后的第7条记录,由于其中不再包含row numer 1这个必要条件,因此直接就丢弃了后面的查询,也就是说,8-10条记录也不会再去fetch了,因为没有一条可以获取到rownum=1
同样的,当我们要获取后5条记录,即5<rownum<11的记录,直接使用以下查询是获取不到值的:
SQL> select rownum,object_id,object_name from test1 where rownum>5 and rownum <11;
no rows selected
为什么呢?原因刚才已经解释过了,由于rownum>5中首先就排除掉了rownum=1这条记录,因此就直接丢弃这些行,也就没有返回结果了,也就是一直去找row number 1,但永远也找不到(因为被条件排除掉了),即使把rownum<11这个条件写在前面,结果也是一致的,注意这里连接条件用的是and,如果用or那就相当于把rownum>5这个条件忽略了,是会有结果的
SQL> select object_id,object_name from test1 where rownum<11 or rownum>5;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#          3 I_OBJ#         25 PROXY_ROLE_DATA$         39 I_IND1         51 I_CDEF2         26 I_PROXY_ROLE_DATA$_1
10 rows selected.

即使使用between ... and ...也是不行的,看执行结果:
SQL> select object_id,object_name from test1 where rownum between 0 and 5;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#
SQL> select object_id,object_name from test1 where rownum between 1 and 5;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------         20 ICOL$         44 I_USER1         28 CON$         15 UNDO$         29 C_COBJ#
SQL> select object_id,object_name from test1 where rownum between 6 and 10;

no rows selected
between0和1都是包含有rownum=1的,因此有结果,而between 6就相当于直接rownum>5,把rownum=1排除掉了,所以就无结果

由于rownum的这个伪列的特殊性,因此在我们书写SQL语句时,尤其是写分页代码的时候,必须使用子查询来获取后5条记录,如:
SQL> select b.object_id,b.object_name from (select rownum rn,a.* from test1 a where rownum<11) b where rn>5;
 OBJECT_ID OBJECT_NAME ---------- ------------------------------          3 I_OBJ#         25 PROXY_ROLE_DATA$         39 I_IND1         51 I_CDEF2         26 I_PROXY_ROLE_DATA$_1
当然,我们也可以用rowid来直接获取后5条记录,因为rowid没有rownum那样的特殊要求,但实际操作起来,是非常不方便的,实际环境中也没有这样的用法,rowid难以记忆也不方便书写
SQL> select object_id,object_name from test1 where rowid>to_char('AAAM+rAAGAAAACUAAE') and rowid<=to_char('AAAM+rAAGAAAACUAAJ');
 OBJECT_ID OBJECT_NAME ---------- ------------------------------          3 I_OBJ#         25 PROXY_ROLE_DATA$         39 I_IND1         51 I_CDEF2         26 I_PROXY_ROLE_DATA$_1
总结:
rowid通常不直接用于SQL代码中,主要是给索引使用的,通过索引查找rowid,再更具rowid回表取数据,使Oracle数据库完成高效的数据检索,而rownum则经常用在数据库的应用代码中,尤其是对分页数据的过滤,但使用的时候必须要注意rownum的特性,切忌跳过对rownum=1的获取而造成无结果集的问题










oracle数据库中rowid与rownum有什不同?

rowid在记录创建时就生成了,而且是不变的,直接指向硬件上的存储位置,能用rowid直接访问是最快的,但也是人力所无法做到的。rownum是个伪列,查询的时候除非特别指定,否则不会显示。其主要的用处是控制查询返回的行数,比如在WHERE中加ROWNUM<5,则查询结果如果在5行或以上时,只返回前4行。
 

网上很多人说oracle 11g在处理大数据分页时用rowid比rownum效率快很多,rowid与rownum分页效率原理

rownum和rowid是两种不同的东西,不知道你如何利用rowid来分页?
rownum是返回的记录编号。rowid可理解为返回记录的实际地址。
当根据rowid访问时相当于不经查询直接取数,用rownum必须经过查询(即数据库里有查询动作)。如果已经知道了rowid再去获取数据和通过rownum计数去获取数据,肯定用rowid快。
实际上,由于oracle不支持的真正的分页查询,所谓分页,是先把数据从数据库中查询出来,然后再把对应页的数据返回给调用者,剩余的数据扔掉了。所以,这种情况下,注定用rowid不如用rownum快。
 

www.htsjk.Com true http://www.htsjk.com/shujukunews/3723.html NewsArticle 使用rowid和rownum获取记录时要注意的问题,rowidrownum 我们知道,rowid和rownum在Oracle中都是可以被当做伪劣使用的,主要用来定位表中特定的记录,但它们是有区别的,rowid是和行记录的物...
评论暂时关闭