Oracle中rownum&rowid

做过Oracle分页的人都知道由于Oracle中没有像MySql中limit函数以及SQLServer中的top关键字等,所以只能通过伪列的方式去满足分页功能,在此,不谈分页方法,只从根本上去介绍这两个伪列的用法与原理

rownum和rowid都是伪列,但是两者的根本是不同的

  • rownum是根据sql查询出的结果给每行分配一个逻辑编号,所以你的sql不同也就会导致最终rownum不同
  • rowid是物理结构上的,在每条记录insert到数据库中时,都会有一个唯一的物理记录 (不会变)

    下面以scott用户的emp表为例.

一、ROWNUM伪列

ROWNUM伪列是Oracle首先进行查询获取到结果集之后在加上去的一个伪列,这个伪列对符合条件的结果添加一个从1开始的序列号,先看一个例子:

SELECT ROWNUM,empno,ename,job FROM emp WHERE deptno = 30;

结果如下:


可以看到确实添加了一列从1开始的序列号,那么有了这个伪列,就可以完成好多提取数据的工作,比如提取emp表中前5条数据,SQL如下:

SELECT ROWNUM,empno,ename,job FROM emp WHERE ROWNUM < 6;

ROWNUM是动态的,也就是必须先有查询到的结果集,然后再给这个结果集加上一个列,比如给结果集中的第一条数据的ROWNUM的值为1,依次类推,如果此时这样写:

SELECT ROWNUM,empno,ename,job FROM emp WHERE ROWNUM > 5 AND ROWNUM <= 10;
当生成结果集时,Oracle首先会产生一条ROWNUM为1的记录,显然不符合条件,那么同样会继续产生第二条数据,同样标识ROWNUM为1,该条记录同样继续被过滤掉,后续生成的ROWNUM依然为1,因此上述查询语句不会有任何查询结果,所以如果想要使上述结果有满足条件的结果集,必须使用子查询(把rownum查出来后放在一个虚表中作为这个虚表的字段再根据条件查询),代码如下:

SELECT * FROM (SELECT ROWNUM nums,emp.* FROM emp) WHERE nums > 5 AND nums <= 10;
子查询部分固定表里面数据的记录编号,外层查询过滤子查询里面固定的记录编号就可以实现Oracle分页!


二、ROWID伪列

同ROWNUM伪列不同的是,它是物理存在的,ROWID是一种数据类型,它使用基于64bit编码的18个字符来唯一标识一条记录物理位置的一个ID,类似于Java中一个对象的哈希码,都是为了唯一标识对应对象的物理位置,需要注意的是ROWID虽然可以在表中进行查询,但是其值并未存储在表中(既然是伪列,就说明表中并不会物理存储ROWID的值),所以不支持增删改操作,下面看个例子:

SELECT ROWNUM,ROWID,empno,ename,job FROM emp WHERE ROWNUM <= 5;
结果如下:


可以看到ROWID确实由18个字符组成,组成结构如下:

数据对象编号 文件编号 块编号 行编号
OOOOOO FFF BBBBBB RRR
至于ROWID的作用,由于ROWID用来唯一标识表中数据的唯一性,所以可以利用这个特性去除重复,举个例子,首先运行下述两行代码:

  1. CREATE TABLE dept_bak AS SELECT * FROM dept;
  2. INSERT INTO dept_bak SELECT * FROM dept;
得到一个如下的数据库表


很明显,数据有重复的,当然你也可以用一个很原始的方法,就是将有重复记录的表中的数据导到另外一张表中,最后再倒回去。

SQL>create table emp_tmp as select distinct* from emp;
SQL>truncate table emp;        //清空表记录
SQL>insert into empselect * from emp_tmp;//将临时表中的数据添加回原表

但是要是emp的表数据是百万级或是更大的千万级的,那这样的方法显然是不明智的,因此我们可以根据rowid来处理,rowid具有唯一性,查询时效率是很高的,那么就可以利用这个特性去重,简单示例代码如下:

 DELETE FROM dept_bak 
     WHERE ROWID NOT IN(SELECT MIN(ROWID) 
                            FROM dept_bak GROUP BY DEPTNO); 

再例如,学生表中的姓名会有重复的情况,但是学生的学号是不会重复的,如果我们要删除学生表中姓名重复只留学号最大的学生的记录,怎么办呢?可以参考如下做法:

DELETE FROM stu a
    WHERE ROWID NOT IN (SELECT MAX(ROWID)
                          FROM stu b
                         WHERE a.name = b.name
                           AND a.stno < b.stno);

除了上述之外还有好多作用,详情参照Oracle官方文档!,在此不多做叙述!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章