Oracle ROWNUM用法和分页查询总结

**********************************************************************************************************

[转载]

Oracle的分页查询语句基本上可以按照本文给出的格式来进行套用。

Oracle分页查询格式(一):http://yangtingkun.itpub.net/post/468/100278

Oracle分页查询格式(二):http://yangtingkun.itpub.net/post/468/101703

Oracle分页查询格式(三):http://yangtingkun.itpub.net/post/468/104595

Oracle分页查询格式(四):http://yangtingkun.itpub.net/post/468/104867

Oracle分页查询格式(五):http://yangtingkun.itpub.net/post/468/107934

Oracle分页查询格式(六):http://yangtingkun.itpub.net/post/468/108677

Oracle分页查询格式(七):http://yangtingkun.itpub.net/post/468/109834

Oracle分页查询格式(八):http://yangtingkun.itpub.net/post/468/224557

Oracle分页查询格式(九):http://yangtingkun.itpub.net/post/468/224409

Oracle分页查询格式(十):http://yangtingkun.itpub.net/post/468/224823

Oracle分页查询的排序问题:http://yangtingkun.itpub.net/post/468/112274

Oracle官网连接查询优化的说明:http://docs.oracle.com/cd/E11882_01/server.112/e16638/optimops.htm#i36235

NESTED LOOP/HASH JOIN/SORT MERGE JOIN的区别:http://jewfinkl.blog.163.com/blog/static/14076982012431052316/

**********************************************************************************************************

根据以上文章进行了如下的总结。

ROWNUM

可能都知道ROWNUM只适用于小于或小于等于,如果进行等于判断,那么只能等于1,不能进行大于的比较。

ROWNUM是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推。

ROWNUM总是从1开始,不管当前的记录是否满足查询结果,ROWNUM返回的值都是1,如果这条记录的值最终满足所有的条件,那么ROWNUM会递加,下一条记录的ROWNUM会返回2,否则下一条记录的ROWNUM仍然返回1。

理解了这一点,就清楚为什么一般的ROWNUM大于某个值或等于某个不为1的值是无法返回结果的,因此对于每条记录的ROWNUM都是1,而ROWNUM为1不满足查询的结果,所以下一条记录的ROWNUM不会递增,仍然是1,因此所有的记录都不满足条件。

分页查询格式1

在查询的最外层控制分页的最小值和最大值。查询语句如下:

SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM TABLE_NAME) A
)
WHERE RN BETWEEN 21 AND 40

分页查询格式2

SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM TABLE_NAME) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21

分页查询格式3

考虑到多表联合的情况,如果不介意在系统中使用HINT的话,可以将分页的查询语句改写为:

SELECT /*+ FIRST_ROWS */ * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM TABLE_NAME) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21

效率问题

对比这两种写法,绝大多数的情况下,第2个查询的效率比第1个高得多。

这是由于CBO优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第2个查询语句,第二层的查询条件WHERE ROWNUM <= 40就可以被Oracle推入到内层查询中,这样Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。

而第1个查询语句,由于查询条件BETWEEN 21 AND 40是存在于查询的第三层,而Oracle无法将第三层的查询条件推到最内层(即使推到最内层也没有意义,因为最内层查询不知道RN代表什么)。因此,对于第1个查询语句,Oracle最内层返回给中间层的是所有满足条件的数据,而中间层返回给最外层的也是所有数据。数据的过滤在最外层完成,显然这个效率要比第一个查询低得多。

上面分析的查询不仅仅是针对单表的简单查询,对于最内层查询是复杂的多表联合查询或最内层查询包含排序的情况一样有效。

观察上面格式1和格式2二者的执行计划可以发现,两个执行计划唯一的区别就是格式2的查询在COUNT这步使用了STOPKEY,也就是说,Oracle将ROWNUM <= 20推入到查询内层,当符合查询的条件的记录达到STOPKEY的值,则Oracle结束查询。因此,可以预见,采用第二种方式,在翻页的开始部分查询速度很快,越到后面,效率越低,当翻到最后一页,效率应该和第一种方式接近。

分页查询语句之所以可以很快的返回结果,是因为它的目标是最快的返回第一条结果。如果每页有20条记录,目前翻到第5页,那么只需要返回前100条记录都可以满足查询的要求了,也许还有几万条记录也符合查询的条件,但是由于分页的限制,在当前的查询中可以忽略这些数据,而只需尽快的返回前100条数据。这也是为什么在标准分页查询语句中经常会使用FIRST_ROWS提示的原因。

对于行操作,可以在得到结果的同时将结果直接返回给上一层调用。但是对于结果集操作,Oracle必须得到结果集中所有的数据,因此分页查询中所带的ROWNUM信息不起左右。如果最内层的子查询中包含了下面这些操作中的一个以上,则分页查询语句无法体现出任何的性能优势:UNION、UNION ALL、MINUS、INTERSECT、GROUP BY、DISTINCT、UNIQUE以及聚集函数如MAX、MIN和分析函数等。

Oracle10g的新功能GROUP BY STOPKEY,使得Oracle10g解决了GROUP BY操作分页效率低的问题。在10g以前,Oracle的GROUP BY操作必须完全执行完,才能将结果返回给用户。但是Oracle10g增加了GROUP
BY STOPKEY执行路径,使得用户在执行GROUP BY操作时,可以根据STOPKEY随时中止正在运行的操作。这使得标准分页函数对于GROUP BY操作重新发挥了作用。

除了这些操作以外,分页查询还有一个很明显的特点,就是处理的页数越小,效率就越高,越到后面,查询速度越慢。

分页查询用来提高返回速度的方法都是针对数据量较小的前N条记录而言。无论是索引扫描,NESTED LOOP连接,还是ORDER BY STOPKEY,这些方法带来性能提升的前提都是数据量比较小,一旦分页到了最后几页,会发现这些方法不但没有办法带来性能的提升,而且性能比普通查询还要低得多。这一点,在使用分页查询的时候,一定要心里有数。

分页查询一般情况下,很少会翻到最后一篇,如果只是偶尔碰到这种情况,对系统性能不会有很大的影响,但是如果经常碰到这种情况,在设计分页查询时应该给予足够的考虑。

多表联合

下面简单讨论一下多表联合的情况。对于最常见的等值表连接查询,CBO一般可能会采用两种连接方式NESTED LOOP和HASH JOIN(MERGE JOIN效率比HASH JOIN效率低,一般CBO不会考虑)。

一般对于大表查询情况下,HASH JOIN的效率要比NESTED LOOP高很多,所以CBO一般默认会选择HASH JOIN.

但是如果分页查询的内层是这种连接查询的话,使用NESTED LOOP可以更快的得到前N条记录。

在这里,由于使用了分页,因此指定了一个返回的最大记录数,NESTED LOOP在返回记录数超过最大值时可以马上停止并将结果返回给中间层,而HASH JOIN必须处理完所有结果集(MERGE JOIN也是)。那么在大部分的情况下,对于分页查询选择NESTED LOOP作为查询的连接方法具有较高的效率(分页查询的时候绝大部分的情况是查询前几页的数据,越靠后面的页数访问几率越小)。

HASH JOIN中第一步也就是第一张表的全表扫描是无法应用STOPKEY的,这就是NESTED LOOP比HASH JOIN优势的地方。

但是,如果恰好第一张表很小,对这张表的全扫描的代价极低,会显得HASH JOIN效率更高。

如果两张表的大小相近,或者Oracle错误的选择了先扫描大表,则使用HASH JOIN的效率就会低得多。

因此对于表连接来说,在写分页查询的时候,可以考虑增加FIRST_ROWS提示,它会导致CBO选择NESTED LOOP,有助于更快的将查询结果返回。

其实,不光是表连接,对于所有的分页查询都可以加上FIRST_ROWS提示。

不过需要注意的时,分页查询的目标是尽快的返回前N条记录,因此,无论是ROWNUM还是FIRST_ROWS机制都是提高前几页的查询速度,

对于分页查询的最后几页,采用HASH JOIN的方式,执行效率几乎没有任何改变,而采用NESTED LOOP方式,则效率严重下降,而且远远低于HASH JOIN的方式。

排序列不唯一所带来的问题

如果用来排序的列不唯一,也就是存在值相等的行,可能会造成第一次在前10条返回记录中,某行数据出现了,而第二次在11到第20条记录中,某行数据又出现了。一条数据重复出现两次,就必然意味着有数据在两次查询中都不会出现。

其实造成这个问题的原因很简单,是由于排序列不唯一造成的。Oracle这里使用的排序算法不具有稳定性,也就是说,对于键值相等的数据,这种算法完成排序后,不保证这些键值相等的数据保持排序前的顺序。

解决这个问题其实也很简单。有两种方法可以考虑。

1)在使用不唯一的字段排序时,后面跟一个唯一的字段。

一般在排序字段后面跟一个主键就可以了,如果表不存在主键,跟ROWID也可以。这种方法最简单,且对性能的影响最小。

2)另一种方法就是使用前面给出过多次的BETWEEN AND的方法。

这种方式由于采用表数据的全排序,每次只取全排序中的某一部分数据,因此不会出现上面提到的重复数据问题。

但是正是由于使用了全排序,而且ROWNUM信息无法推到查询内部,导致这种写法的执行效率很低

测试结果

下面做一些测试,按照如下步骤准备数据:

CREATE TABLE T AS SELECT * FROM DBA_USERS;
CREATE TABLE T1 AS SELECT * FROM DBA_SOURCE;
ALTER TABLE T ADD CONSTRAINT PK_T PRIMARY KEY (USERNAME);
ALTER TABLE T1 ADD CONSTRAINT FK_T1_OWNER FOREIGN KEY (OWNER) REFERENCES T(USERNAME);
CREATE INDEX IND_T1_OWNER ON T1(OWNER);
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T')
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T1')
set autotrace traceonly
set timing on

现在表格T中有37行数据,表格T1中有623K行数据。

比较格式1和格式2的查询计划

--查询语句1
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T1) A
)
WHERE RN BETWEEN 21 AND 40;
--查询语句2
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T1) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;
  执行计划 执行时间 统计信息
查询语句1
----------------------------------------------------------

Plan hash value: 3921461035

----------------------------------------------------------------------------

| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------

|   0 | SELECT STATEMENT    |      |   623K|  1231M|  2879   (1)| 00:00:35 |

|*  1 |  VIEW               |      |   623K|  1231M|  2879   (1)| 00:00:35 |

|   2 |   COUNT             |      |       |       |            |          |

|   3 |    TABLE ACCESS FULL| T1   |   623K|    59M|  2879   (1)| 00:00:35 |

----------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN"<=40 AND "RN">=21)

00: 00: 02.40 1  recursive calls

0  db block gets

10441  consistent gets

10435  physical reads

0  redo size

1720  bytes sent via SQL*Net to client

431  bytes received via SQL*Net from client

3  SQL*Net roundtrips to/from client

0  sorts (memory)

0  sorts (disk)

20  rows processed

查询语句2
----------------------------------------------------------------------------

| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------

|   0 | SELECT STATEMENT    |      |    40 | 82800 |     2   (0)| 00:00:01 |

|*  1 |  VIEW               |      |    40 | 82800 |     2   (0)| 00:00:01 |

|*  2 |   COUNT STOPKEY     |      |       |       |            |          |

|   3 |    TABLE ACCESS FULL| T1   |    40 |  4000 |     2   (0)| 00:00:01 |

----------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN">=21)

2 - filter(ROWNUM<=40)

00: 00: 00.03 0  recursive calls

0  db block gets

6  consistent gets

20  physical reads

0  redo size

1720  bytes sent via SQL*Net to client

431  bytes received via SQL*Net from client

3  SQL*Net roundtrips to/from client

0  sorts (memory)

0  sorts (disk)

20  rows processed

关联查询

--查询语句1
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T, T1 WHERE T.USERNAME = T1.OWNER) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;
--查询语句2
SELECT /*+ FIRST_ROWS */ * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T, T1 WHERE T.USERNAME = T1.OWNER) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;
--或者
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT /*+ USE_NL(T T1) */ * FROM T, T1 WHERE T.USERNAME = T1.OWNER) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;

可以看到默认是采用hash join,改用nested loop join方式似乎效率并没有明显提高,但是这是由于表T比较小只有34行,所以hash join的第一步即使对T进行全表扫描而无法应用stopkey,效率也很高。

  执行计划 执行时间 统计信息
查询语句1
-----------------------------------------------------------------------------

| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------

|   0 | SELECT STATEMENT     |      |    40 |   165K|     6  (17)| 00:00:01 |

|*  1 |  VIEW                |      |    40 |   165K|     6  (17)| 00:00:01 |

|*  2 |   COUNT STOPKEY      |      |       |       |            |          |

|*  3 |    HASH JOIN         |      |    40 | 12400 |     6  (17)| 00:00:01 |

|   4 |     TABLE ACCESS FULL| T    |    34 |  3740 |     3   (0)| 00:00:01 |

|   5 |     TABLE ACCESS FULL| T1   |    40 |  4000 |     2   (0)| 00:00:01 |

-----------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN">=21)

2 - filter(ROWNUM<=40)

3 - access("T"."USERNAME"="T1"."OWNER")

00: 00: 00.04 0 recursive calls

0 db block gets

9 consistent gets

20 physical reads

0 redo size

2927 bytes sent via SQL*Net to client

431 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

20 rows processed

查询语句2
-----------------------------------------------------------------------------------------------

| Id  | Operation                      | Name         | Rows  | Bytes | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT               |              |    40 |   165K| 13627   (1)| 00:02:44 |

|*  1 |  VIEW                          |              |    40 |   165K| 13627   (1)| 00:02:44 |

|*  2 |   COUNT STOPKEY                |              |       |       |            |          |

|   3 |    NESTED LOOPS                |              |       |       |            |          |

|   4 |     NESTED LOOPS               |              |   623K|   124M| 13627   (1)| 00:02:44 |

|   5 |      TABLE ACCESS FULL         | T            |    34 |  3740 |     3   (0)| 00:00:01 |

|*  6 |      INDEX RANGE SCAN          | IND_T1_OWNER | 36684 |       |    91   (0)| 00:00:02 |

|   7 |     TABLE ACCESS BY INDEX ROWID| T1           | 18342 |  1791K|   710   (1)| 00:00:09 |

-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN">=21)

2 - filter(ROWNUM<=40)

6 - access("T"."USERNAME"="T1"."OWNER")

00: 00: 00.01 1 recursive calls

0 db block gets

14 consistent gets

0 physical reads

0 redo size

2927 bytes sent via SQL*Net to client

431 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

20 rows processed

现在增大表T,

ALTER TABLE T MODIFY(USER_ID NULL, ACCOUNT_STATUS NULL, DEFAULT_TABLESPACE NULL,
TEMPORARY_TABLESPACE NULL, CREATED NULL, PROFILE NULL);
INSERT INTO T(USERNAME) SELECT ('USER' || LEVEL) FROM DUAL CONNECT BY LEVEL < 100000;
COMMIT;
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T')

然后重新测试语句1,会发现现在oracle已经改成用nested loop join了。

因此现在语句1和语句2的效果等同了。可以使用 USE_HASH(T T1) HINT强制使用hash join,结果做下对比,会发现hash join的效率低于nested loop join,读数据发生的IO(consistent gets+physical reads)大大增加了.

可以看到CBO是相当智能了。

含排序的查询

含排序操作的分页查询。可以简单的将查询分为两种不同情况,第一种排序列就是索引列,这种可以利用索引读取,第二种排序列没有索引。

第一种情况又可以细分为:完全索引扫描和通过索引扫描定位到表记录两种情况。无论是那种情况,都可以通过索引的全扫描来避免排序的产生。

第二种情况下,排序不可避免,但是利用给出分页格式,Oracle不会对所有数据进行排序,而是只排序前N条记录。

--查询语句1,排序列就是索引列.注意这里需要加上OWNER IS NOT NULL,否则由于OWNER列不是NOT NULL,会导致索引无法使用。
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T1 WHERE OWNER IS NOT NULL ORDER BY OWNER) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;

--查询语句2,排序列没有索引
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T1 ORDER BY NAME) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21;

--查询语句3,排序列没有索引
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM T1 ORDER BY NAME) A
)
WHERE RN BETWEEN 21 AND 40;
  执行计划 执行时间 统计信息
查询语句1
-----------------------------------------------------------------------------------------------

| Id  | Operation                      | Name         | Rows  | Bytes | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT               |              |    40 | 82800 |     4   (0)| 00:00:01 |

|*  1 |  VIEW                          |              |    40 | 82800 |     4   (0)| 00:00:01 |

|*  2 |   COUNT STOPKEY                |              |       |       |            |          |

|   3 |    VIEW                        |              |    40 | 82280 |     4   (0)| 00:00:01 |

|   4 |     TABLE ACCESS BY INDEX ROWID| T1           |   646K|    62M|     4   (0)| 00:00:01 |

|*  5 |      INDEX FULL SCAN           | IND_T1_OWNER |    40 |       |     3   (0)| 00:00:01 |

-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN">=21)

2 - filter(ROWNUM<=40)

5 - filter("OWNER" IS NOT NULL)

*排序列就是索引列,可以看到通过索引的全扫描来避免了排序的产生。

00: 00: 00.01 1 recursive calls

0 db block gets

8 consistent gets

1 physical reads

0 redo size

1682 bytes sent via SQL*Net to client

427 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

20 rows processed

查询语句2

-----------------------------------------------------------------------------------------

| Id  | Operation                | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

-----------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT         |      |    40 | 82800 |       | 18077   (1)| 00:03:37 |

|*  1 |  VIEW                    |      |    40 | 82800 |       | 18077   (1)| 00:03:37 |

|*  2 |   COUNT STOPKEY          |      |       |       |       |            |          |

|   3 |    VIEW                  |      |   646K|  1268M|       | 18077   (1)| 00:03:37 |

|*  4 |     SORT ORDER BY STOPKEY|      |   646K|    62M|    72M| 18077   (1)| 00:03:37 |

|   5 |      TABLE ACCESS FULL   | T1   |   646K|    62M|       |  3023   (1)| 00:00:37 |

-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN">=21)

2 - filter(ROWNUM<=40)

4 - filter(ROWNUM<=40)

*排序列没有索引,排序不可避免。带STOPKEY的ORDER BY,排序操作放到了内存中,

在大数据量需要排序的情况下,要比不带STOPKEY排序的效率高得多。

00: 00: 01.32 1 recursive calls

0 db block gets

10973 consistent gets

10969 physical reads

0 redo size

2529 bytes sent via SQL*Net to client

427 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

20 rows processed

查询语句3
--------------------------------------------------------------------------------------

| Id  | Operation             | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

--------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT      |      |   646K|  1276M|       | 18077   (1)| 00:03:37 |

|*  1 |  VIEW                 |      |   646K|  1276M|       | 18077   (1)| 00:03:37 |

|   2 |   COUNT               |      |       |       |       |            |          |

|   3 |    VIEW               |      |   646K|  1268M|       | 18077   (1)| 00:03:37 |

|   4 |     SORT ORDER BY     |      |   646K|    62M|    72M| 18077   (1)| 00:03:37 |

|   5 |      TABLE ACCESS FULL| T1   |   646K|    62M|       |  3023   (1)| 00:00:37 |

--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter("RN"<=40 AND "RN">=21)

*排序列没有索引,排序不可避免,不带STOPKEY,

进行的数据的全排序,排序数据量大,排序操作不得不在磁盘上完成,因此耗时比较多。

00: 00: 05.31 72 recursive calls

26 db block gets

10973 consistent gets

19933 physical reads

0 redo size

6489 bytes sent via SQL*Net to client

427 bytes received via SQL*Net from client

3 SQL*Net roundtrips to/from client

0 sorts (memory)

1 sorts (disk)

20 rows processed

排序列不唯一所带来的问题

[email protected]> CREATE TABLE TEST AS SELECT ROWNUM ID, A.* FROM DBA_OBJECTS A;
Table created.

[email protected]> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'TEST');
PL/SQL procedure successfully completed.

[email protected]> COLUMN OBJECT_NAME FORMAT A30
[email protected]> SELECT * FROM
  2  (
  3  SELECT A.*, ROWNUM RN
  4  FROM (SELECT ID, OWNER, OBJECT_NAME FROM TEST WHERE OWNER IS NOT NULL ORDER BY OWNER) A
  5  WHERE ROWNUM <= 10
  6  )
  7  WHERE RN >= 1;

        ID OWNER                          OBJECT_NAME                            RN
---------- ------------------------------ ------------------------------ ----------
     69170 APEX_030200                    WWV_FLOW_INIT_HTP_BUFFER                1
     69179 APEX_030200                    WWV_HTF                                 2
     69178 APEX_030200                    WWV_FLOW_LANG                           3
     69177 APEX_030200                    WWV_FLOW_UTILITIES                      4
     69176 APEX_030200                    VC4000ARRAY                             5
     69175 APEX_030200                    WWV_FLOW_SECURITY                       6
     69174 APEX_030200                    WWV_FLOW                                7
     69173 APEX_030200                    HTMLDB_ITEM                             8
     69172 APEX_030200                    WWV_FLOW_GLOBAL                         9
     69171 APEX_030200                    WWV_FLOW_IMAGE_PREFIX                  10

10 rows selected.

[email protected]> SELECT * FROM
  2  (
  3  SELECT A.*, ROWNUM RN
  4  FROM (SELECT ID, OWNER, OBJECT_NAME FROM TEST WHERE OWNER IS NOT NULL ORDER BY OWNER) A
  5  WHERE ROWNUM <= 20
  6  )
  7  WHERE RN >= 11;

        ID OWNER                          OBJECT_NAME                            RN
---------- ------------------------------ ------------------------------ ----------
     69180 APEX_030200                    WWV_HTP                                11
     69179 APEX_030200                    WWV_HTF                                12
     69178 APEX_030200                    WWV_FLOW_LANG                          13
     69177 APEX_030200                    WWV_FLOW_UTILITIES                     14
     69176 APEX_030200                    VC4000ARRAY                            15
     69175 APEX_030200                    WWV_FLOW_SECURITY                      16
     69174 APEX_030200                    WWV_FLOW                               17
     69173 APEX_030200                    HTMLDB_ITEM                            18
     69172 APEX_030200                    WWV_FLOW_GLOBAL                        19
     69171 APEX_030200                    WWV_FLOW_IMAGE_PREFIX                  20

10 rows selected.
--可以看到,有多个ID在两次查询中都出现了。
--通过加上ID作为排序列解决这个问题。

[email protected]> SELECT * FROM
  2  (
  3  SELECT A.*, ROWNUM RN
  4  FROM (SELECT ID, OWNER, OBJECT_NAME FROM TEST WHERE OWNER IS NOT NULL ORDER BY OWNER, ID) A
  5  WHERE ROWNUM <= 10
  6  )
  7  WHERE RN >= 1;

        ID OWNER                          OBJECT_NAME                            RN
---------- ------------------------------ ------------------------------ ----------
     69170 APEX_030200                    WWV_FLOW_INIT_HTP_BUFFER                1
     69171 APEX_030200                    WWV_FLOW_IMAGE_PREFIX                   2
     69172 APEX_030200                    WWV_FLOW_GLOBAL                         3
     69173 APEX_030200                    HTMLDB_ITEM                             4
     69174 APEX_030200                    WWV_FLOW                                5
     69175 APEX_030200                    WWV_FLOW_SECURITY                       6
     69176 APEX_030200                    VC4000ARRAY                             7
     69177 APEX_030200                    WWV_FLOW_UTILITIES                      8
     69178 APEX_030200                    WWV_FLOW_LANG                           9
     69179 APEX_030200                    WWV_HTF                                10

10 rows selected.

[email protected]> SELECT * FROM
  2  (
  3  SELECT A.*, ROWNUM RN
  4  FROM (SELECT ID, OWNER, OBJECT_NAME FROM TEST WHERE OWNER IS NOT NULL ORDER BY OWNER, ID) A
  5  WHERE ROWNUM <= 20
  6  )
  7  WHERE RN >= 11;

        ID OWNER                          OBJECT_NAME                            RN
---------- ------------------------------ ------------------------------ ----------
     69180 APEX_030200                    WWV_HTP                                11
     69181 APEX_030200                    ESCAPE_SC                              12
     69182 APEX_030200                    WWV_FLOW_META_DATA                     13
     69183 APEX_030200                    WWV_FLOW_TEMPLATES_UTIL                14
     69184 APEX_030200                    WWV_RENDER_CALENDAR2                   15
     69185 APEX_030200                    WWV_RENDER_CHART2                      16
     69186 APEX_030200                    WWV_FLOW_CHECK                         17
     69187 APEX_030200                    WWV_RENDER_REPORT3                     18
     69188 APEX_030200                    WWV_FLOW_PAGE_CACHE_API                19
     69189 APEX_030200                    WWV_FLOW_RENDER_QUERY                  20

10 rows selected.
时间: 2024-11-09 18:53:42

Oracle ROWNUM用法和分页查询总结的相关文章

oracle count 百万级 分页查询记要总数、总条数优化

oracle count 百万级 分页查询记录总数.总条数优化 oracle count 百万级 查询记录总数.总条数优化 最近做一个项目时,做分页时,发现分页查询速度很慢,分页我做的是两次查询,一次是查询总数,一次是查询分页结果 /** 查询总记录数 **/ SELECT COUNT(id) FROM USER order by id /** 查询结果集 **/ select * from ( select row_.*, rownum rownum_ from ( select id , u

Oracle基础及三层分页查询

一.数据库表空间和数据文件 解析:一个数据库下可以开N个表空间,一个表空间可以包含N个数据文件.表空间是逻辑概念. 二.关于listener.ora位置 修改该界面上的数据,会影响指定路径的监听配置文件 D:\app\asus\product\11.2.0\dbhome_2\NETWORK\ADMIN 三.数据库和实例的关系 数据库(database):物理操作系统文件或磁盘(disk)的集合.使用Oracle 10g的自动存储管理(Automatic Storage Management,AS

详解Oracle的几种分页查询语句

分页查询格式: SELECT * FROM  ( SELECT A.*, ROWNUM RN  FROM (SELECT * FROM TABLE_NAME) A  WHERE ROWNUM <= 40 ) WHERE RN >= 21 其中最内层的查询SELECT * FROM TABLE_NAME表示不进行翻页的原始查询语句.ROWNUM <= 40和RN >= 21控制分页查询的每页的范围. 上面给出的这个分页查询语句,在大多数情况拥有较高的效率.分页的目的就是控制输出结果集

MySQL、SqlServer、Oracle三大主流数据库分页查询 (MySQL分页不能用top,因为不支持)

一. MySQL 数据库 分页查询MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数, 第一个参数指定返回的第一行在所有数据中的位置,从0开始(注意不是1),第二个参数指定最多返回行数.例如:select * from table WHERE … LIMIT 10; #返回前10行select * from table WHERE … LIMIT

MySQL的limit用法和分页查询的性能分析及优化

申明:本博文转发于 点击链接跳转 一.limit用法在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能. SELECT * FROM table LIMIT [offset,] rows | `rows OFFSET offset ` (LIMIT offset, `length`)SELECT*FROM tablewhere condition1 = 0and condition2 = 0and condition3

MySQL、Oracle和SQL Server的分页查询语句

原文地址:http://www.cnblogs.com/ginponson/p/5746435.html 假设当前是第PageNo页,每页有PageSize条记录,现在分别用Mysql.Oracle和SQL Server分页查询student表. 1.Mysql的分页查询 SELECT * FROM student LIMIT (PageNo - 1) * PageSize,PageSize; 理解:(Limit n,m)  =>从第n行开始取m条记录,n从0开始算. 2.Oracel的分页查询

Oracle分页查询语句的写法(转)

分页查询是我们在使用数据库系统时经常要使用到的,下文对Oracle数据库系统中的分页查询语句作了详细的介绍,供您参考. Oracle分页查询语句使我们最常用的语句之一,下面就为您介绍的Oracle分页查询语句的用法,如果您对此方面感兴趣的话,不妨一看. Oracle分页查询语句基本上可以按照本文给出的格式来进行套用.Oracle分分页查询格式: SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (SELECT * FROM TABLE_NAME) A WHER

Oracle数据库中的分页--rownum

1. 介绍 当我们在做查询时,经常会遇到如查询限定行数或分页查询的需求,MySQL中可以使用LIMIT子句完成,在MSSQL中可以使用TOP子句完成,那么在Oracle中,我们如何实现呢? Oracle提供了一个rownum的伪列,它会根据返回记录生成一个序列化的数字. rownum和rowid都是伪列,但是两者的根本是不同的.rownum是根据SQL查询出的结果给每行分配一个逻辑编号,所以SQL不同也就会导致最终rownum不同:rowid是物理结构上的,在每条记录INSERT到数据库中时,都

SQL两种简单分页查询方式

以前我们或许都用过了linq的skip and take方式进行分页查询,但是很少自己写sql的分页查询,因为大多数时候,我们都是在调用别人的方法. 最近看到一个文档,感觉方法里面实现的分页查询进入数据库调用的时候,实际最底层调用的还是SQL的分页查询,例如,我们用linq写个分页查询,转成sql表达式后发现: 实际调用的时候,才发现SQL底层是这样进行分页的. 下面,本文主要是介绍两种SQL的分页查询. 一,TOP方式 ----第一种分页查询方式:TOP方式 declare @page int