zoukankan      html  css  js  c++  java
  • oracle_SQL中ROWID与ROWNUM的使用(转)

    rownum:
    ROWNUM是对结果集加的一个伪列
    即先查到结果集之后再加上去的一个列 
    (强调:
    1.对内 行行神龙附尾
    从数据库提取记录 结果集的第一行数据,然后 在 加上去rownum 这个列,这个列的值先从1开始生成,对应结果集的第一行,(生成 结果集的第二行数据,然后才生成对应 rownum列第二行的值)然后随行数增加 依次加1,直到 rownum 这一列的值不满足语句中rownum的限制条件, 即出现 false判断,此时,查询到的结果集 在 PL/SQL上只展示到 rownum 不满足条件的上一行
    2. 对外 始终表里如一
    即 如果外界要 用 已包含 rownum语句作为 子语句,那么 子语句 的 rownum列 对与 主语句来说 就是实实在在已经存在的一个 实列,可以用这个实列的值作为条件进行过滤,而此时主语句本身的rownum列仍然是一个伪劣,为了 能让 子语句的rownum列能和主语句的rownum列分开,要 给 子语句的rownum列 起个别名,这样就能够顺利的使用 子语句的rownum 的值了,而 主语句的rownum列 仍然 是 遵守  1.对内行行神龙附尾的 规则 )。
    如:查询 除头两行行 以外的其他行
    SQL>select *from(select rownum no ,id,name from student) where no>2;
     
     
     
    简单的说ROWNUM是符合条件结果的序列号。
    它总是从1开始排起的。
    使用ROWNUM时,只能使用<、<=、!=符号
    (当然 子语句的rownum 列 对于主语句来说 不在此例)
     
     
    1.ROWNUM的使用——TOP-N分析
    使用SELECT语句返回的结果集,若希望按特定条件查询前N条记录,可以使用伪列ROWNUM。
    ROWNUM是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集)。简单的说ROWNUM是符合条件结果的序列号。它总是从1开始排起的。
    使用ROWNUM时,只能使用<、<=、!=符号。
     
    举例:
    student(学生)表,表结构为:
    ID       char(6)      --学号
    name    VARCHAR2(10)   --姓名
    --建表
    create table student(ID char(6), name VARCHAR2(10));
    --添加测试记录
    insert into studentvalues('200001','张一');
    insert into studentvalues('200002','王二');
    insert into studentvalues('200003','李三');
    insert into studentvalues('200004','赵四');
    commit;
    --测试
    SQL> select * fromstudent;
    ID     NAME
    ------------------------------
    200001 张一
    200002 王二
    200003 李三
    200004 赵四
     
    ⑴ rownum对于等于某值的查询条件
    如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。
    但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。
    因为rownum都是从1开始,也就是说 此时 的rownum的限制条件 是 1=2,这肯定是一个 false条件,也就是说 结果集的展示 到此 就为止了 不在往下走了,所有1以上的自然数在rownum做等于判断时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
    SQL> selectrownum,id,name from student where rownum=1;
        ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
             1 200001 张一
     
    SQL> selectrownum,id,name from student where rownum =2;
    未选定行
     
    ⑵rownum对于大于某值的查询条件
    如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录。
    SQL> selectrownum,id,name from student where rownum >2;
    未选定行
     
    那如何才能找到第二行以后的记录呢?可以使用子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
    SQL>select *from(select rownum no ,id,name from student) where no>2;
            NO ID     NAME
    ---------- ---------------------------------------------------------
             3 200003 李三
             4 200004 赵四
     
    SQL> select *from(select rownum,id,name from student)where rownum>2;
    未选定行
     
    ⑶ rownum对于小于某值的查询条件
    如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n(n>1的自然数)的条件认为是成立的,所以可以找到记录。
    SQL> selectrownum,id,name from student where rownum <3;
        ROWNUM ID     NAME
    -------------------------- ---------------------------------------------------
    1 200001 张一
                   2200002 王二
     
    综上几种情况,可能有时候需要查询rownum在某区间的数据,可以看出rownum对小于某值的查询条件是为true的,rownum对于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之间的数据,包括第二行和第三行数据,那么我们可以写以下语句,先让它返回小于等于3的记录行,然后在主查询中判断新的rownum的别名列大于等于2的记录行。但是这样的操作会在大数据集中影响速度。
    SQL> select * from(select rownum no,id,name from student where rownum<=3 ) where no >=2;
            NO ID     NAME
    ---------- ------ ---------------------------------------------------
             2 200002 王二
             3 200003 李三
     
    ⑷ rownum和排序
    Oracle中的rownum是在取数据的时候产生的序号,所以想对指定排序的数据指定rowmun行数据就必须注意了。
    SQL> select rownum,id,name from student order by name;
        ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
             3 200003 李三
             2 200002 王二
             1 200001 张一
             4 200004 赵四
     
    可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询
    SQL> select rownum,id,name from (select * from student order by name);
        ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
             1 200003 李三
             2 200002 王二
             3 200001 张一
             4 200004 赵四
     
    这样就成了按name排序,并且用rownum标出正确序号(由小到大)。order by name 如果name是主键或有索引,查询出来的rownum完全按照1,2,3.....的次序。
    SQL> alter tablestudent add constraint pk_stu primary key(name);
    SQL> select rownum,id,name from student order by name;
    ROWNUM ID NAME
    ---------- ---------------------------------------------------------
    1 200003 李三
    2 200002 王二
    3 200001 张一
    4 200004 赵四
     
     
    2.ROWID的使用——快速删除重复的记录
    ROWID是数据的详细地址,通过rowid,oracle可以快速的定位某行具体的数据的位置。
    ROWID可以分为物理rowid和逻辑rowid两种。普通的表中的rowid是物理rowid,索引组织表(IOT)的rowid是逻辑rowid。
    当表中有大量重复数据时,可以使用ROWID快速删除重复的记录。
    举例:
    --建表tbl
    SQL> create table stu(no number,name varchar2(10),sexchar(2));
    --添加测试记录
    SQL> insert into stu values(1, 'ab',’男’);
    SQL> insert into stu values(1, 'bb',’女’);
    SQL> insert into stu values(1, 'ab',’男’);
    SQL> insert into stu values(1, 'ab',’男’);
           SQL>commit;
     
    删除重复记录方法很多,列出两种。
    ⑴ 通过创建临时表
    可以把数据先导入到一个临时表中,然后删除原表的数据,再把数据导回原表,SQL语句如下:
    SQL>create table stu_tmp as select distinct* from stu;
    SQL>truncate table sut;                                                  //清空表记录
    SQL>insert into stu  select * from stu_tmp;                       //将临时表中的数据添加回原表
     
    这种方法可以实现需求,但是很明显,对于一个千万级记录的表,这种方法很慢,在生产系统中,这会给系统带来很大的开销,不可行。
     
    ⑵ 利用rowid结合max或min函数
    使用rowid快速唯一确定重复行结合max或min函数来实现删除重复行。
    SQL>delete from stua where rowid not in (select max(b.rowid) from stu b where a.no=b.no and a.name= b.name and a.sex = b.sex);                                          //这里max使用min也可以
    或者用下面的语句
    SQL>delete from stu a where rowid < (select max(b.rowid) from stu b where a.no=b.no and a.name =b.name and a.sex = b.sex);       //这里如果把max换成min的话,前面的where子句中需要把"<"改为">"
     
    跟上面的方法思路基本是一样的,不过使用了group by,减少了显性的比较条件,提高效率。
    SQL>delete from stu where rowid not in (select max(rowid) from stu t group by t.no, t.name, t.sex);
     
    思考:若在stu表中唯一确定任意一行数据(1,'ab',’男’),把sex字段更新为”女”,怎么做?
           SQL>update stu set sex=’女’where rowid=(select min(rowid) from stu where no=1 and name=’ab’ and sex=’男’);
     
    oracle伪列 rowid和rownum
    一,什么是伪列RowID?
    1,首先是一种数据类型,唯一标识一条记录物理位置的一个id,基于64位编码的18个字符显示。
    2,未存储在表中,可以从表中查询,但不支持插入,更新,删除它们的值。
     
    二,RowID的用途
    1,在开发中使用频率应该是挺多的,特别在一些update语句中使用更加频繁。所以oracleERP中大部份的视图都会加入rowid这个字段。
       在一些cursor定义时也少不了加入rowid。但往往我们在开发过程中,由于连接的表很多,再加上程序的复制,有时忽略了rowid对应的是那一个表中rowid,所以有时过程出错,
       往往花上很多时间去查错,最后查出来既然是update时带的rowid并非此表的rowid,所以在发现很多次的错误时,重视rowid起来了,开发中一定要注意rowid的匹配
    2,能以最快的方式访问表中的一行。
    3,能显示表的行是如何存储的。
    4,作为表中唯一标识。
     
    三,RowID的组成
    rowid确定了每条记录是在Oracle中的哪一个数据对象,数据文件、块、行上。
    ROWID 的格式如下:
       数据对象编号        文件编号        块编号            行编号
      OOOOOO             FFF                BBBBBB    RRR
     
     
     AAAWBM AAJ AABs4T AAA
    AAAWBMAAJAABs4TAAB
       由 data_object_id# + rfile# + block#+ row#   组成,占用10个bytes的空间,
       32bit的data_object_id#,
       10 bit 的 rfile#, 
       22bit 的 block#,
       16 bit 的 row#. 
       所以每个表空间不能超过1023个 数据文件。
      bytes:字节 ,8个bit
     bit :位
     
    四,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 GroupBy empno);
        查找少量重复记录
       select * from empa a where rowid<>(select max(rowid) from empawhere empno=a.empno );
     
       删除重复记录的几种方法:
       (1).适用于有大量重复记录的情况(列上建有索引的时候,用以下语句效率会很高):
       Delete empa Where empno In (Select empno From empa Group By empno HavingCount(*) > 1)
       And ROWID Not In (Select Min(ROWID) From empa Group By empno HavingCount(*) > 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 whereempno=a.empno );
     
    ---------------------------------------------------------------------------------------------------------------------------------------------------
     
    注意:rownum从1开始;
     
              rownum按照记录插入时的顺序给记录排序,所以有order by的子句时一定要注意啊!
     
              使用时rownum,order by字段是否为主键有什么影响?
     
              子查询中rownum rn,而rn用到外查询中到底是怎样的序列?
     
               若id主键是按照从小到大的顺序插入的,select语句没有group by 和order by的子句时,rownum的顺序和id顺序基本一致。
     
     
     
    对于 Oracle 的 rownum 问题,很多资料都说不支持>,>=,=,between...and,只能用以上符号(<、<=、!=),并非说用>,>=,=,between..and时会提示SQL语法错误,而是经常是查不出一条记录来,还会出现似乎是莫名其妙的结果来,其实您只要理解好了这个 rownum 伪列的意义就不应该感到惊奇,同样是伪列,rownum 与 rowid 可有些不一样,下面以例子说明
     
    假设某个表 t1(c1) 有 20 条记录
     
    如果用 selectrownum,c1 from t1 where rownum < 10, 只要是用小于号,查出来的结果很容易地与一般理解在概念上能达成一致,应该不会有任何疑问的。
     
    可如果用 selectrownum,c1 from t1 where rownum > 10 (如果写下这样的查询语句,这时候在您的头脑中应该是想得到表中后面10条记录),你就会发现,显示出来的结果要让您失望了,也许您还会怀疑是不谁删了一些记录,然后查看记录数,仍然是 20 条啊?那问题是出在哪呢?
     
    先好好理解 rownum 的意义吧。因为ROWNUM是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集)。简单的说 rownum 是对符合条件结果的序列号。它总是从1开始排起的。所以你选出的结果不可能没有1,而有其他大于1的值。所以您没办法期望得到下面的结果集:
     
    11 aaaaaaaa
    12 bbbbbbb
    13 ccccccc
    .................
     
    rownum >10 没有记录,因为第一条不满足去掉的话,第二条的ROWNUM又成了1,所以永远没有满足条件的记录。或者可以这样理解:
     
    ROWNUM是一个序列,是oracle数据库从数据文件或缓冲区中读取数据的顺序。它取得第一条记录则rownum值为1,第二条为2,依次类推。如果你用>,>=,=,between...and这些条件,因为从缓冲区或数据文件中得到的第一条记录的rownum为1,则被删除,接着取下条,可是它的rownum还是1,又被删除,依次类推,便没有了数据。
     
    有了以上从不同方面建立起来的对 rownum 的概念,那我们可以来认识使用 rownum 的几种现像
     
    1. select rownum,c1 from t1 where rownum !=10 为何是返回前9条数据呢?它与 select rownum,c1 fromtablename 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 > 10 这种条件的话话就要用嵌套语句,把 rownum 先生成,然后对他进行查询。
    select *
    from (selet rownum as rn,t1.* from a where ...)
    where rn >10
     
    一般代码中对结果集进行分页就是这么干的。
     
    另外:rowid 与 rownum 虽都被称为伪列,但它们的存在方式是不一样的,rowid可以说是物理存在的,表示记录在表空间中的唯一位置ID,在DB中唯一。只要记录没被搬动过,rowid是不变的。rowid 相对于表来说又像表中的一般列,所以以 rowid 为条件就不会有 rownum那些情况发生。
    另外还要注意:rownum不能以任何基表的名称作为前缀。
     
    对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,且rownum不能以任何表的名称作为前缀。
     
    (1) rownum 对于等于某值的查询条件
    如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
    SQL> select rownum,id,name from studentwhere rownum=1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标)
    SQL> select rownum,id,name from studentwhere rownum =2;
       ROWNUM ID     NAME
     
    (2)rownum对于大于某值的查询条件
       如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录。
     
    查找到第二行以后的记录可使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
    SQL>select * from(select rownum no,id,name from student) where no>2;
           NO ID     NAME
    ---------- ---------------------------------------------------------
            3 200003 李三
            4 200004 赵四
     
    (3)rownum对于小于某值的查询条件
    rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。
    SQL> select rownum,id,name from studentwhere rownum <3;
       ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
           1 200001 张一
           2 200002 王二
     
    查询rownum在某区间的数据,必须使用子查询。例如要查询rownum在第二行到第三行之间的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记录行。但是这样的操作会在大数据集中影响速度。
    SQL> select * from (select rownumno,id,name from student where rownum<=3 ) where no >=2;
           NO ID     NAME
    ---------- ---------------------------------------------------------
             2 200002 王二
            3 200003 李三
     
    (4)rownum和排序  
    Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。
    SQL> select rownum ,id,name from studentorder by name;
       ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
            3 200003 李三
            2 200002 王二
            1 200001 张一
            4 200004 赵四
    可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询;
    SQL> select rownum ,id,name from (select* from student order by name);
       ROWNUM ID     NAME
    ---------- ---------------------------------------------------------
            1 200003 李三
            2 200002 王二
            3 200001 张一
            4 200004 赵四
    这样就成了按name排序,并且用rownum标出正确序号(有小到大)
    笔者在工作中有一上百万条记录的表,在jsp页面中需对该表进行分页显示,便考虑用rownum来作,下面是具体方法(每页显示20条):
    “select * fromtabname where rownum<20 order by name" 但却发现oracle却不能按自己的意愿来执行,而是先随便取20条记录,然后再orderby,后经咨询oracle,说rownum确实就这样,想用的话,只能用子查询来实现先排序,后rownum,方法如下:
    "select * from (select * from tabnameorder by name) where rownum<20",但这样一来,效率会低很多。
    后经笔者试验,只需在orderby 的字段上加主键或索引即可让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 topn
     
       由于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
     
       01first
     
       02Second
     
       03third
     
       04forth
     
       05fifth
     
       06sixth
     
       07seventh
     
       08eighth
     
       09ninth
     
       10last
     
       则按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
  • 相关阅读:
    父子进程 signal 出现 Interrupted system call 问题
    一个测试文章
    《淘宝客户端 for Android》项目实战 html webkit android css3
    Django 中的 ForeignKey ContentType GenericForeignKey 对应的数据库结构
    coreseek 出现段错误和Unigram dictionary load Error 新情况(Gentoo)
    一个 PAM dbus 例子
    漫画统计学 T分数
    解决 paramiko 安装问题 Unable to find vcvarsall.bat
    20141202
    js
  • 原文地址:https://www.cnblogs.com/Sunnor/p/4553113.html
Copyright © 2011-2022 走看看