Oracle 的sql陷阱(1)rownum和order by一起使用

简介: rownum和order by一起使用可能会遇到取数不准确的问题
#Oracle 的sql陷阱(1)rownum和order by一起使用
rownum和order by一起使用可能会遇到取数不准确的问题,客户遇到了,我也测试了下,临时解决办法是使用嵌套查询,先排序出来结果再rownum,这种效率不高,当然最高效的是oracle优化器自己知道如何去取你要的数据,但是有时却不是100%准确,因为他不是一个100%的公式逻辑。
##客户遇到的问题
某天,客户突然发现取到的数据不对了,生成数据不对了,客户的情况如下:
以前一直正常执行,且有类似业务结构,仅表名不同的表也还取数正常。
###遇到问题的表T_WCPB现在的执行计划

SQL> SELECT DJH0  FROM ERP_CC.T_WCPB WHERE ROWNUM = 1 ORDER BY DJH0 desc;
DJH0

CB15081800001
Execution Plan
----------------------------------------------------------
Plan hash value: 678339681
------------------------------------------------------------------------------------
| Id  | Operation           | Name       | Rows  | Bytes | Cost (%CPU)| Time       |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |       |     1 |    14 |     3  (34)| 00:00:01 |
|   1 |  SORT ORDER BY           |       |     1 |    14 |     3  (34)| 00:00:01 |
|*  2 |   COUNT STOPKEY        |       |       |       |        |       |
|   3 |    INDEX FAST FULL SCAN| PK_T_WCPB |     1 |    14 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(ROWNUM=1)
从执行计划中我们看到oracle通过主键索引快速扫描取到所需数据后“COUNT STOPKEY”,再进行的排序“SORT ORDER BY”,rownum排在前面,肯定不是我们想要的。但是话说回来,你想要的是排序后的rownum,如果你想要的是rownum<n 的结果再排序呢?oracle 到底该如何决定先取数还是先排序?

###匪夷所思
ROWNUM = 1 两个表取到的一个是该表的最大值,一个是最小值

SQL> SELECT DJH1  FROM ERP_CC.T_WXSCKD WHERE ROWNUM = 1;
DJH1
-------------
CK17081600121
Execution Plan
----------------------------------------------------------
Plan hash value: 577535013
--------------------------------------------------------------------------------
-----
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time
    |
--------------------------------------------------------------------------------
-----
|   0 | SELECT STATEMENT      |         |      1 |     14 |      2   (0)| 00:00
:01 |
|*  1 |  COUNT STOPKEY          |         |        |        |         |
    |
|   2 |   INDEX FAST FULL SCAN| PK_T_WXSCKD |      1 |     14 |      2   (0)| 00:00
:01 |
--------------------------------------------------------------------------------
-----
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM=1)
Statistics
----------------------------------------------------------
      0  recursive calls
      0  db block gets
      7  consistent gets
      0  physical reads
      0  redo size
    533  bytes sent via SQL*Net to client
    524  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed
SQL> SELECT DJH0  FROM ERP_CC.T_WCPB WHERE ROWNUM = 1;
DJH0
-------------
CB15081800001
Execution Plan
----------------------------------------------------------
Plan hash value: 44983662
--------------------------------------------------------------------------------
---
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time
  |
--------------------------------------------------------------------------------
---
|   0 | SELECT STATEMENT      |       |    1 |    14 |    2   (0)| 00:00:0
1 |
|*  1 |  COUNT STOPKEY          |       |      |      |           |
  |
|   2 |   INDEX FAST FULL SCAN| PK_T_WCPB |    1 |    14 |    2   (0)| 00:00:0
1 |
--------------------------------------------------------------------------------
---
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM=1)
Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      7  consistent gets
      0  physical reads
      0  redo size
    533  bytes sent via SQL*Net to client
    524  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed
SQL>


###测试
测试表 tab1 ,开始没有设置主键,100记录
SYS@orcl1>select STUID from  tab1 where rownum=1;
     STUID
----------
     1
SYS@orcl1>select STUID from  tab1 where rownum=1 order by 1 desc;
     STUID
----------
     1
Execution Plan
----------------------------------------------------------
Plan hash value: 1612508337
--------------------------------------------------------------------------------
------------
| Id  | Operation        | Name           | Rows  | Bytes | Cost (%CPU)
| Time       |
--------------------------------------------------------------------------------
------------
|   0 | SELECT STATEMENT    |               |     1 |     3 |     3  (34)
| 00:00:01 |
|   1 |  SORT ORDER BY        |               |     1 |     3 |     3  (34)
| 00:00:01 |
|*  2 |   COUNT STOPKEY     |               |       |       |
|       |
|   3 |    TABLE ACCESS FULL| tab1 |     1 |     3 |     2   (0)
| 00:00:01 |
--------------------------------------------------------------------------------
------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(ROWNUM=1)
Statistics
----------------------------------------------------------
     19  recursive calls
      0  db block gets
     13  consistent gets
      0  physical reads
      0  redo size
    523  bytes sent via SQL*Net to client
    520  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      4  sorts (memory)
      0  sorts (disk)
      1  rows processed
SYS@orcl1>

没有主键,rownum=1 取到的是最小值;加上排序,取到的也是1,执行计划看出来,是先取数再排序的。

添加主键alter table tab1 add constraint pk_STUID  primary key(STUID);

此时执行sql
SYS@orcl1>select STUID from  tab1 where rownum=1;
     STUID
----------
     1

有主键,rownum=1 取到的仍是最小值;
SYS@orcl1>set autot on
SYS@orcl1>select STUID from  tab1 where rownum=1 order by 1 desc;
     STUID
----------
       100
Execution Plan
----------------------------------------------------------
Plan hash value: 1084965663
--------------------------------------------------------------------------------
--------
| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Ti
me     |
--------------------------------------------------------------------------------
--------
|   0 | SELECT STATEMENT        |           |     1 |     3 |     1     (0)| 00
:00:01 |
|*  1 |  COUNT STOPKEY            |           |       |       |        |
       |
|   2 |   INDEX FULL SCAN DESCENDING| PK_STUID |     1 |     3 |     1     (0)| 00
:00:01 |
--------------------------------------------------------------------------------
--------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM=1)
Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      5  consistent gets
      0  physical reads
      0  redo size
    523  bytes sent via SQL*Net to client
    520  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      1  sorts (memory)
      0  sorts (disk)
      1  rows processed
再加上排序,虽然rown=1一个条件是取到是最小值,但是先走的排序再取的rownum,这是我们预期的。


再来看看 rownum <50的情况
SYS@orcl1>select STUID from  tab1 where rownum<50 order by 1 desc;
     STUID
----------
       100
    99
    98
    97
    96
    95
    94
    93
    92
    91
    90
     STUID
----------
    89
    88
    87
    86
    85
    84
    83
    82
    81
    80
    79
     STUID
----------
    78
    77
    76
    75
    74
    73
    72
    71
    70
    69
    68
     STUID
----------
    67
    66
    65
    64
    63
    62
    61
    60
    59
    58
    57
     STUID
----------
    56
    55
    54
    53
    52
49 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1084965663
--------------------------------------------------------------------------------
--------
| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Ti
me     |
--------------------------------------------------------------------------------
--------
|   0 | SELECT STATEMENT        |           |    49 |   147 |     1     (0)| 00
:00:01 |
|*  1 |  COUNT STOPKEY            |           |       |       |        |
       |
|   2 |   INDEX FULL SCAN DESCENDING| PK_STUID |    49 |   147 |     1     (0)| 00
:00:01 |
--------------------------------------------------------------------------------
--------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<50)
Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      5  consistent gets
      0  physical reads
      0  redo size
       1379  bytes sent via SQL*Net to client
    553  bytes received via SQL*Net from client
      5  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
     49  rows processed
SYS@orcl1>
##对于客户情况,先对该情况做一个sql调整,使用嵌套查询
SYS@orcl1>select * from (select STUID from prvflat_stuinfo_part order by 1 desc ) where rownum=1;
     STUID
----------
       100
Execution Plan
----------------------------------------------------------
Plan hash value: 825904777
--------------------------------------------------------------------------------
---------
| Id  | Operation             | Name    | Rows    | Bytes | Cost (%CPU)| T
ime    |
--------------------------------------------------------------------------------
---------
|   0 | SELECT STATEMENT         |        |     1 |    13 |     1   (0)| 0
0:00:01 |
|*  1 |  COUNT STOPKEY             |        |    |    |         |
    |
|   2 |   VIEW                 |        |     1 |    13 |     1   (0)| 0
0:00:01 |
|   3 |    INDEX FULL SCAN DESCENDING| PK_STUID |   100 |   300 |     1   (0)| 0
0:00:01 |
--------------------------------------------------------------------------------
---------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM=1)
Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      1  consistent gets
      0  physical reads
      0  redo size
    523  bytes sent via SQL*Net to client
    520  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed
SYS@orcl1>


#这个是时候,oracle逻辑似乎非常明显,只要order的字段是主键,就是先排序再取rownum,真的是这样吗?测试看来是正确的,但是客户遇到的又是怎样情况?难道是个别表的bug?

相关文章
|
24天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL隐式游标:数据的“自动导游”与“轻松之旅”
【4月更文挑战第19天】Oracle PL/SQL中的隐式游标是自动管理的数据导航工具,简化编程工作,尤其适用于简单查询和DML操作。它自动处理数据访问,提供高效、简洁的代码,但不适用于复杂场景。显式游标在需要精细控制时更有优势。了解并适时使用隐式游标,能提升数据处理效率,让开发更加轻松。
|
24天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标自定义异常:数据探险家的“专属警示灯”
【4月更文挑战第19天】Oracle PL/SQL中的游标自定义异常是处理数据异常的有效工具,犹如数据探险家的警示灯。通过声明异常名(如`LOW_SALARY_EXCEPTION`)并在满足特定条件(如薪资低于阈值)时使用`RAISE`抛出异常,能灵活应对复杂业务规则。示例代码展示了如何在游标操作中定义和捕获自定义异常,提升代码可读性和维护性,确保在面对数据挑战时能及时响应。掌握自定义异常,让数据管理更从容。
|
24天前
|
SQL Oracle 安全
Oracle的PL/SQL游标异常处理:从“惊涛骇浪”到“风平浪静”
【4月更文挑战第19天】Oracle PL/SQL游标异常处理确保了在数据操作中遇到的问题得以优雅解决,如`NO_DATA_FOUND`或`TOO_MANY_ROWS`等异常。通过使用`EXCEPTION`块捕获并处理这些异常,开发者可以防止程序因游标问题而崩溃。例如,当查询无结果时,可以显示定制的错误信息而不是让程序终止。掌握游标异常处理是成为娴熟的Oracle数据管理员的关键,能保证在复杂的数据环境中稳健运行。
|
24天前
|
SQL Oracle 安全
Oracle的PL/SQL异常处理方法:守护数据之旅的“魔法盾”
【4月更文挑战第19天】Oracle PL/SQL的异常处理机制是保障数据安全的关键。通过预定义异常(如`NO_DATA_FOUND`)和自定义异常,开发者能优雅地管理错误。异常在子程序中抛出后会向上传播,直到被捕获,提供了一种集中处理错误的方式。理解和善用异常处理,如同手持“魔法盾”,确保程序在面对如除数为零、违反约束等挑战时,能有效保护数据的完整性和程序的稳定性。
|
24天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL中FOR语句循环游标的奇幻之旅
【4月更文挑战第19天】在Oracle PL/SQL中,FOR语句与游标结合,提供了一种简化数据遍历的高效方法。传统游标处理涉及多个步骤,而FOR循环游标自动处理细节,使代码更简洁、易读。通过示例展示了如何使用FOR循环游标遍历员工表并打印姓名和薪资,对比传统方式,FOR语句不仅简化代码,还因内部优化提升了执行效率。推荐开发者利用这一功能提高工作效率。
|
24天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标属性:数据的“导航仪”与“仪表盘”
【4月更文挑战第19天】Oracle PL/SQL游标属性如同车辆的导航仪和仪表盘,提供丰富信息和控制。 `%FOUND`和`%NOTFOUND`指示数据读取状态,`%ROWCOUNT`记录处理行数,`%ISOPEN`显示游标状态。还有`%BULK_ROWCOUNT`和`%BULK_EXCEPTIONS`增强处理灵活性。通过实例展示了如何在数据处理中利用这些属性监控和控制流程,提高效率和准确性。掌握游标属性是提升数据处理能力的关键。
|
24天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL显式游标:数据的“私人导游”与“定制之旅”
【4月更文挑战第19天】Oracle PL/SQL中的显式游标提供灵活精确的数据访问,与隐式游标不同,需手动定义、打开、获取和关闭。通过DECLARE定义游标及SQL查询,OPEN启动查询,FETCH逐行获取数据,CLOSE释放资源。显式游标适用于复杂数据处理,但应注意SQL效率、游标管理及异常处理。它是数据海洋的私人导游,助力实现业务逻辑和数据探险。
|
关系型数据库 Oracle
oracle的rownum与having用法 去除重复 在重复情况用rownum
一般来说,大家会用rownum,也就是伪列来指定要显示多条数据, 比如 select linename from aced where rownum
1497 0
|
Oracle 关系型数据库 移动开发
ORACLE 中ROWNUM用法总结!
ORACLE 中ROWNUM用法总结! 对于 Oracle 的 rownum 问题,很多资料都说不支持>,>=,=,between...and,只能用以上符号(=,=,between..and 时会提示SQL语法错误,而是经常是查不出一条记录来,还会出现似乎是莫名其妙的结果来,其实您只要理...
1105 0
|
13天前
|
DataWorks Oracle 关系型数据库
DataWorks操作报错合集之尝试从Oracle数据库同步数据到TDSQL的PG版本,并遇到了与RAW字段相关的语法错误,该怎么处理
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
30 0