标题:Oracle中的rownum不能使用大于>的问题

一、对rownum的说明

关于Oracle 的 rownum 问题,很多资料都说不支持SQL语句中的“>、>=、=、between...and”运算符,只能用如下运算符号“<、<=、!=”,

并非说用“>、>=、=、between..and”时会提示SQL语法错误,而是经常是查不出一条记录来,还会出现似乎是莫名其妙的结果来。

其实,只要理解好了这个 rownum 伪列的意义就不应该感到惊奇。

rowid 与 rownum 虽都被称为伪列,但它们的存在方式是不一样的:

rowid 是物理存在的,表示记录在表空间中的唯一位置ID,在DB中是唯一的。只要记录没被搬动过,rowid是不变的。

rowid 相对于表来说又像表中的一般列,所以,以 rowid 为条件就不会有rownum那些莫名其妙的结果出现。

另外还要注意:rownum不能以任何基表的名称作为前缀。

对于下面的SQL语句

SQL>select rownum,id,age,name from loaddata where rownum > 2;

ROWNUM ID     AGE NAME
    ------- ------ --- ------

rownum>2,没有查询到任何记录。

因为rownum总是从1开始的,第一条不满足去掉的话,第二条的rownum 又成了1。依此类推,所以永远没有满足条件的记录。

可以这样理解:rownum是一个序列,是Oracle数据库从数据文件或缓冲区中读取数据的顺序。

它取得第一条记录则rownum值为1,第二条为2。依次类推。

当使用“>、>=、=、between...and”这些条件时,从缓冲区或数据文件中得到的第一条记录的rownum为1,不符合sql语句的条件,会被删除,接着取下条。

下条的rownum还会是1,又被删除,依次类推,便没有了数据。

二、对rownum使用中几种现象的分析说明
 
    有了以上从不同方面建立起来的对rownum的概念,下面认识使用rownum的几种现象:

(1) select rownum,id,age,name from loaddata where rownum != 10 为何是返回前9条数据呢?
     为什么它与 select rownum,id,age,name from loaddata where rownum < 10 返回的结果集是一样的?

因为是在查询到结果集后,显示完第9条记录后,之后的记录都是 != 10或者 >=10,所以只显示前面9条记录。

也可以这样理解,rownum为9后,取的记录的rownum为10,因条件为 !=10,所以删掉。然后取下一条,其rownum又是10,也删掉。以此类推。

所以只会显示前面9条记录。

(2)什么rownum >1时查不到一条记录,而 rownum >0或rownum >=1 却总显示所有记录。

这是因为rownum是在查询到的结果集后,再加上去的,它总是从1开始的。

(3)为什么between 1 and 10 或者 between 0 and 10 能查到结果,而用 between 2 and 10 却得不到结果。

原因同上:因为 rownum总是从1开始。

从上可得,任何时候想把rownum = 1这条记录抛弃是不对的。它在结果集中是不可或缺的。

少了rownum=1就像空中楼阁一般不能存在。所以,rownum条件要包含到1。

三、一些rownum实际运用的例子:

-----------
     --sql建表脚本

create table LOADDATA
     (
         ID   VARCHAR2(50),
         AGE  VARCHAR2(50),
         NAME VARCHAR2(50)
     );
     -----------

(1) rownum 对于“等于某值”的查询条件

如果希望找到loaddata表中第一条记录的信息,可以使用rownum=1作为条件。

但是想找到loaddata表中第二条记录的信息,使用rownum=2,是查不到数据的。

因为rownum都是从“1”开始。

“1”以上的自然数,在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。

select rownum,id,age,name
      from loaddata
      where rownum = 1;   --可以用在限制返回记录条数的地方,保证不出错,如:隐式游标。

SQL>select rownum,id,age,name from loaddata where rownum = 1;

ROWNUM ID     AGE NAME
    ------- ------ --- ------
         1 200001 22   AAA

SQL>select rownum,id,age,name from loaddata where rownum = 2;

ROWNUM ID     AGE NAME
    ------- ------ --- ------

注:SQL>select rownum,id,age,name from loaddata where rownum != 3; --返回的是前2条记录。

ROWNUM ID     AGE NAME
    ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22   BBB

(2)rownum对于大于某值的查询条件

如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的。

原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录。

SQL>select rownum,id,age,name from loaddata where rownum > 2;

ROWNUM ID     AGE NAME
    ------- ------ --- ------

那如何才能找到第二行以后的记录?

可以使用下面的子查询方法来解决。

注意子查询中的rownum必须要有别名,否则仍然会查不到记录,这是因为rownum不是某个表的列。

如果不起别名的话,无法知道rownum是子查询的列,还是主查询的列。

SQL>select rownum,id,age,name from(select rownum no ,id,age,name from loaddata) where no > 2;

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         3 200003 22   CCC
         4 200004 22   DDD
         5 200005 22   EEE
         6 200006 22   AAA

SQL>select * from(select rownum,id,age,name from loaddata) where rownum > 2;

ROWNUM ID     AGE NAME
     ------- ------ --- ------

(3)rownum对于小于某值的查询条件

如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。

显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。

SQL> select rownum,id,age,name from loaddata where rownum < 3;

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22 BBB

综上几种情况:

可能有时候需要查询rownum在某区间的数据,从上可以看出rownum对小于某值的查询条件是认为true的。

rownum对于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的,那就必须使用子查询。

例如要查询rownum在第二行到第三行之间的数据,包括第二行和第三行数据,那么只能写以下语句,先让它返回小于等于三的记录行,

然后在主查询中判断新的rownum的“别名列”大于等于二的记录行。但是这样的操作会在大数据集中影响到检索速度。

SQL>select * from (select rownum no,id,age,name from loaddata where rownum <= 3 ) where no >= 2; --必须是里小外大

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         2 200002 22 BBB
         3 200003 22   CCC

也可以用这种方法实现:

SQL>select rownum,id,age,name from loaddata where rownum < 4
         minus
         select rownum,id,age,name from loaddata where rownum < 2

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         2 200002 22 BBB
         3 200003 22   CCC

(4)rownum和排序

Oracle中的rownum的是在取数据的时候产生的序号。故,如在已排序的数据中,要求取出指定的rowmun行数据时,就需注意了。

前提条件:loaddata表中已经insert了5条记录,最后一条记录id是200005,接着insert into loaddata values('200006','22','AAA');

SQL>select rownum,id,age,name from loaddata;

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22   BBB
         3 200003 22   CCC
         4 200004 22   DDD
         5 200005 22   EEE
         6 200006 22   AAA

SQL>select rownum ,id,age,name from loaddata order by name;

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         6 200006 22   AAA
         2 200002 22   BBB
         3 200003 22   CCC
         4 200004 22   DDD
         5 200005 22   EEE

可以看出,rownum并不是按照name列来生成的序号。

系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。

为了解决这个问题,必须使用子查询

SQL>select rownum ,id,age,name from (select * from loaddata order by name);

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200006 22   AAA
         3 200002 22   BBB
         4 200003 22   CCC
         5 200004 22   DDD
         6 200005 22   EEE

这样就成了按name排序,并且用rownum标出正确序号(有小到大)。

对于大数据量的时候,建议在order by 的字段上加主键或索引这样效率会提高很多。

同样,返回中间的记录集:

SQL>select * from ( select rownum ro,id,age,name from
loaddata where rownum < 5 order by name ) where ro > 2; (先选再排序再选)

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         3 200002 22   BBB
         4 200003 22   CCC

一般业务需求中,是需要先排序后,再返回中间记录集:

SQL>select * from (select t.*,rownum ro from (select
id,age,name from loaddata order by name ) t where rownum < 5 ) where
ro>=2; (先排序再选再选)

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         3 200002 22   BBB
         4 200003 22   CCC

注意此时的SQL语句写法,使用了多重(三层)嵌套。同时注意:rownum使用了“列别名”。
 
     实际上,该语句也是Oracle数据集一个经典的SQL语句分页算法:先排序,再选择rownum < 某页的最大值,再选择rownum > 某页的最小值。

四、一个实例:

需求:假设不知道数据库里的数据规则和数量,要把所有的student数据打印到终端。

解:rownum是伪列,在表里没有,数据库先是执行from book遍历book表。

如果没有where条件过滤,则先做成一个结果集,然后再看select后面的条件,挑出合适的字段形成最后的结果集。

如果有 where条件,则不符合条件的就会从第一个结果集中删除,后面的数据继续加进来判断。

所以如果直接写rownum=2,或者rownum>10这样的语句就查不出数据。

可以用一个子查询来解决这个问题:对于select rownum,id from book where rownum=2; 是查不出数据来的。

declare
           v_number binary_integer;
           v_student student%rowtype;
       begin
           select count(*) into v_number from student;
           for i in 1..v_number loop
              select id,name,age into v_student from ( select rownum rn,id,name,age from student ) where rn=i;
              dbms_output.put_line('id: '||v_student.id||' name:'||v_student.name);
          end loop;
       end;

rownum是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集)。

简单的说 rownum 是对符合条件结果的序列号。

它总是从1开始排起的。所以,选出的结果不可能没有1,反而有其他大于1的值。

Oracle中的rownum不能使用大于>的问题的更多相关文章

  1. 转:Oracle中的rownum不能使用大于>的问题

    一.对rownum的说明 关于Oracle 的 rownum 问题,很多资料都说不支持SQL语句中的“>.>=.=.between...and”运算符,只能用如下运算符号“<.< ...

  2. Oracle中的rownum用法解析

    注意:rownum从1开始:  1.rownum按照记录插入时的顺序给记录排序,所以有order by的子句时一定要注意啊!  2.使用时rownum,order by字段是否为主键有什么影响?  3 ...

  3. Oracle中的rownum,ROWID的 用法

    1.ROWNUM的使用——TOP-N分析 使用SELECT语句返回的结果集,若希望按特定条件查询前N条记录,可以使用伪列ROWNUM. ROWNUM是对结果集加的一个伪列,即先查到结果集之后再加上去的 ...

  4. (转)Oracle中的rownum,ROWID的 用法

    场景:在书写oracle的sql语句时候,如果语句不存在主键,需要删除几条重复的记录,这个时候如果不知道oracle中的伪列,就需要把所有的重复记录先删除,再插入.这样做好麻烦,可以通过伪列来定位记录 ...

  5. Oracle中的rownum 和rowid的用法和区别

    Oracle中的rownum 和rowid的用法和区别   1.rownum是伪列,是在获取查询结果集后再加上去的 (获取一条记录加一个rownum).对符合条件的结果添加一个从1开始的序列号. eg ...

  6. oracle中时间格式时候的大于号是大于和等于的意思

    oracle中时间格式时候的大于号是大于和等于的意思

  7. [转]Oracle中使用Rownum分页详细例子

    原文地址:http://www.jb51.net/article/52272.htm 在MySQL中,我们通常都使用limit来完成数据集获取的分页操作,而在Oracle数据库中,并没有类似limit ...

  8. SQLServer中的top、MySql中的limit、Oracle中的rownum

    (1)在SQL Server中,我们使用 select top N * from tablename来查询tablename表中前N条记录. (2)在MySQL中,我们使用select * from ...

  9. oracle中的rownum

    1.oracle存在rownum关键字,它是一个伪列(系统分配在结果集上的一个特殊列) SELECT * FROM  oracleTable WHERE ROWNUM=1 上面这条语句是能够查询出第一 ...

随机推荐

  1. 在Xcode中手动添加pch文件

    在Xcode中手动添加pch文件: 一: 在工程中新建.pch文件,pch文件名通常用工程名字命名: 二: 在Targets->build Settings->Prefix Header ...

  2. String.Compare 方法 (String, Int32, String, Int32, Int32)

    String.Compare 方法 (String, Int32, String, Int32, Int32) 对两个指定的 String 对象的子字符串进行比较,并返回一个指示二者在排序顺序中的相对 ...

  3. 第2天:JavaScript基础(运算符、案例、循环、冒泡以及prompt提示输入框)

    一元运算在前在后的区别 加加 var num1 = 10; //++在后面 先参与运算 再自加1 var sum1 = num1++ +10; console.log("sum1的值:&qu ...

  4. linux系统mysql主主复制(双主复制)

    一.简介 在上一篇的主从复制中:http://www.cnblogs.com/lay2017/p/9043985.html 我们了解到,mysql通过master写日志,slave读取并执行日志内容从 ...

  5. MyBatis 学习(一)

    一.MyBatis 1.MyBatis 介绍(百度) MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数 ...

  6. 对Mybatis的初步认识

    1.认识Mybatis MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架. MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索. MyBat ...

  7. hibernate cascade的真正含义

    hibernate cascade 是 @OneToOne @OneToMany @ManyToOne @ManyToMany等注解的属性,表示级联操作. /** * (Optional) The o ...

  8. easyui 删除行的时候 引起的 bug

    问题场景 easyui 编辑里 有删除行的功能 文档提供的方法如下 $aplgrid为grid 的容器 ind 为当前行的索引 $aplgrid.datagrid('deleteRow', ind); ...

  9. 通过JTS源码分析Rtree(未完待续)

    前言 R树在数据库等领域做出的功绩是非常显著的.它很好的解决了在高维空间搜索等问题.它把B树的思想很好的扩展到了多维空间,采用了B树分割空间的思想,并在添加.删除操作时采用合并.分解结点的方法,保证树 ...

  10. SpringBoot如何添加拦截器

    在web开发的过程中,为了实现登录权限验证,我们往往需要添加一个拦截器在用户的的请求到达controller层的时候实现登录验证,那么SpringBoot如何添加拦截器呢? 步骤如下: 1.继承Web ...