[English]
作者:
fuyuncat
来源:
www.HelloDBA.com
ROWNUM是一种伪列,它会根据返回记录生成一个序列化的数字。利用ROWNUM,我们可以生产一些原先难以实现的结果输出,但因为它是伪列的这个特殊性,我们在使用时也需要注意一些事项,不要掉入“陷阱”。下面就介绍一下它的使用技巧及注意事项。
1 特殊结果输出
利用ROWNUM,我们可以做到一些特殊方式的输出。
1.1 Top N结果输出
我们如果希望取输出结果的前面几条数据,通过ROWNUM可以轻松实现:
SQL> select * from t_test42 where rownum <= 5;USERNAME USER_ID CREATED------------------------------ ---------- ---------WOW 71 26-APR-07CS2 70 15-JAN-073 69 01-NOV-06DMP 68 12-OCT-06PROFILER 67 05-SEP-06
但是,如果你希望对一个排序结果取Top N数据的话,使用ROWNUM存在一些“陷阱”,我们后面部分会介绍这些“陷阱”并且说明如何避免。
1.2 分页查询
利用ROWNUM对结果进行分页,下面返回结果中的第6到第10条记录:
SQL> select * from2 (3 select a.*, rownum as rn from css_bl_view a4 where capture_phone_num = '(1) 925-4604800'5 ) b6 where b.rn between 6 and 10;6 rows selected.Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2770 Card=2183 Bytes =7166789)1 0 VIEW (Cost=2770 Card=2183 Bytes=7166789)2 1 COUNT3 2 TABLE ACCESS (FULL) OF 'CSS_BL_VIEW' (Cost=2770 Card=2183 Bytes=1305434)Statistics----------------------------------------------------------0 recursive calls0 db block gets29346 consistent gets29190 physical reads0 redo size7328 bytes sent via SQL*Net to client234 bytes received via SQL*Net from client4 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)5 rows processed
另外一种实现方式:
SQL> select * from css_bl_view a2 where capture_phone_num = '(1) 925-4604800'3 and rownum <= 104 minus5 select * from css_bl_view a6 where capture_phone_num = '(1) 925-4604800'7 and rownum <= 58 ;Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5920 Card=10 Bytes=8970)1 0 MINUS2 1 SORT (UNIQUE) (Cost=2960 Card=10 Bytes=5980)3 2 COUNT (STOPKEY)4 3 TABLE ACCESS (FULL) OF 'CSS_BL_VIEW' (Cost=2770 Card=2183 Bytes=1305434)5 1 SORT (UNIQUE) (Cost=2960 Card=5 Bytes=2990)6 5 COUNT (STOPKEY)7 6 TABLE ACCESS (FULL) OF 'CSS_BL_VIEW' (Cost=2770 Card=2183 Bytes=1305434)Statistics----------------------------------------------------------0 recursive calls0 db block gets62 consistent gets50 physical reads0 redo size7232 bytes sent via SQL*Net to client234 bytes received via SQL*Net from client4 SQL*Net roundtrips to/from client2 sorts (memory)0 sorts (disk)5 rows processed
第三种实现方式:
SQL> select * from2 (3 select a.*, rownum as rn from css_bl_view a4 where capture_phone_num = '(1) 925-4604800'5 and rownum <= 106 ) b7 where b.rn > 5;Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2770 Card=10 Bytes=32830)1 0 VIEW (Cost=2770 Card=10 Bytes=32830)2 1 COUNT (STOPKEY)3 2 TABLE ACCESS (FULL) OF 'CSS_BL_VIEW' (Cost=2770 Card=2183 Bytes=1305434)Statistics----------------------------------------------------------0 recursive calls0 db block gets35 consistent gets30 physical reads0 redo size7271 bytes sent via SQL*Net to client234 bytes received via SQL*Net from client4 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)5 rows processed
这里特地将三种实现方式的查询计划及统计数据打印出来,大家可以比较一下3中方式的性能。
1.3 利用ROWNUM做分组子排序
对于以下表T_TEST4的内容:
OWNER NAME------------------------------------------------------STRMADMIN STREAMS_QUEUEAPARKMAN JOB_QUEUESYS AQ$_AQ_SRVNTFN_TABLE_ESYS AQ$_KUPC$DATAPUMP_QUETAB_EAPARKMAN AQ$_JMS_TEXT_ESTRMADMIN AQ$_STREAMS_QUEUE_TABLE_ESYS AQ$_SCHEDULER$_EVENT_QTAB_E…
如果我们希望结果按照OWNER进行分组后,再对每组中成员进行编号,结果类似如下:
OWNER NO NAME------------------------------------------------------APARKMAN 1 JOB_QUEUE2 AQ$_JMS_TEXT_ESTRMADMIN 1 STREAMS_QUEUE2 AQ$_STREAMS_QUEUE_TABLE_ESYS 1 AQ$_AQ_SRVNTFN_TABLE_E2 AQ$_KUPC$DATAPUMP_QUETAB_E3 AQ$_SCHEDULER$_EVENT_QTAB_E…
在没有ROWNUM时要实现这样的功能会很复杂,但通过ROWNUM我们可以轻松实现:
SQL> SELECT DECODE(ROWNUM-min_sno,0,a.owner,NULL) owner,DECODE(ROWNUM-min_sno,0,1,rownum+1-min_sno) sno, a.name2 FROM (SELECT *3 FROM t_test84 ORDER BY owner, name ) a,5 (SELECT owner, MIN(rownum) min_sno6 FROM( SELECT *7 FROM t_test88 ORDER BY owner, name)9 GROUP BY owner) b10 WHERE a.owner=b.owner;OWNER SNO NAME------------------------------ ---------- ------------------------------APARKMAN 1 JOB_QUEUE2 AQ$_JMS_TEXT_ESTRMADMIN 1 STREAMS_QUEUE2 AQ$_STREAMS_QUEUE_TABLE_ESYS 1 AQ$_AQ_SRVNTFN_TABLE_E2 AQ$_KUPC$DATAPUMP_QUETAB_E3 AQ$_SCHEDULER$_EVENT_QTAB_E4 AQ$_SCHEDULER$_JOBQTAB_E5 AQ$_STREAMS_QUEUE_TABLE_E6 AQ$_SYS$SERVICE_METRICS_TAB_E7 AQ$_AQ_EVENT_TABLE_E8 AQ$_AQ$_MEM_MC_E9 AQ$_ALERT_QT_E10 ALERT_QUE11 AQ_EVENT_TABLE_Q12 SYS$SERVICE_METRICS13 STREAMS_QUEUE14 SRVQUEUE15 SCHEDULER$_JOBQ16 SCHEDULER$_EVENT_QUEUE17 AQ_SRVNTFN_TABLE_QSYSMAN 1 AQ$_MGMT_NOTIFY_QTABLE_E2 MGMT_NOTIFY_QSYSTEM 1 DEF$_AQERROR2 DEF$_AQCALL3 AQ$_DEF$_AQERROR_E4 AQ$_DEF$_AQCALL_EWMSYS 1 AQ$_WM$EVENT_QUEUE_TABLE_E2 WM$EVENT_QUEUE29 rows selected.2 性能
我们很多程序员在确认某个表中是否有相应数据时,喜欢加上ROWNUM=1,其思路就是只要存在一条数据就说明有相应数据,查询就可以直接返回了,这样就能提高性能了。但是在10G之前,使用ROWNUM=1是不能达到预期的性能效果的,而是需要通过<2或<=1作为过滤条件才能达到预期效果,看以下查询计划:
SQL> select * from t_test12 where object_id <1003 and rownum = 1;Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=37 Card=1 Bytes=86)1 0 COUNT (STOPKEY)2 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_TEST1' (Cost=37 Card=89 Bytes=7654)3 2 INDEX (RANGE SCAN) OF 'T_TEST1_PK' (UNIQUE) (Cost=2 Card=89)Statistics----------------------------------------------------------