暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 是否在条件稳定的情况下使用无顺序by和ROWNUM谓词的SELECT结果?

askTom 2017-10-20
223

问题描述

考虑以下简单场景:

准备工作:
1) 假设我们有一个具有一个列名的表用户: 创建表用户 (名称varchar(100));
2) 让我们在那里放一些值:

插入用户 (名称) 值 ('user1');
插入用户 (名称) 值 ('user2');
插入用户 (名称) 值 ('user3');
插入用户 (名称) 值 ('user4');
插入用户 (名称) 值 ('user5');
插入用户 (名称) 值 ('user6');
插入用户 (名称) 值 ('user7');
插入用户 (名称) 值 ('user8');
插入用户 (名称) 值 ('user9');
插入用户 (名称) 值 ('user10');
插入用户 (名称) 值 ('user11');
插入用户 (名称) 值 ('user12');
...
高达10 000 000个值...

问题:
我使用的是旧的但有点 “标准” 的分页方式,没有结束,这里是 (0,5> 用户的分页示例:
选择 * 从 (选择名称,来自用户的ROWNUM r,其中rownum <= 5) 其中r> 0;

有些用户被退回,比方说:

名称R
用户1 1
用户2 2
用户3 3
用户4 4
用户5 5

但 “问题?” 发生时,我改变边界,让我们说 (0,8> 用户:
选择 * from (选择名称,来自用户的ROWNUM r,其中rownum <= 8) 其中r> 0;
名称R
用户3 1
用户4 2
用户5 3
用户6 4
用户7 5
用户8 6
用户9 7
用户10 8

如您所见,返回不同的用户,例如User1不会返回,即使它是在第一个查询中返回的。
这个问题不能用这样简单的数据集复制,但我用13百万行的集合复制。
(0,2 000 000> 的返回结果不包含在 (0,10 000 000) 的返回集中


问题:
正如你所看到的查询没有排序,所以基于定义的结果可以在任何排序,rownum只是剪切结果时谓词应用-所以从我的理解,它不是bug,但需要从你,专家的确认。假设Oracle optimiser只是决定对边界使用不同的执行方式 (0,2 000 000> 与边界为 (0,10 000 000>) 的情况相比,是否有效?如果是这样,唯一的方法是如何在某些排序上使用ROW_NUMBER来修复它,或者在任何rownum之前通过排序?

非常感谢你的回答。

注:
我正在使用Oracle 12.1.0.2。表没有主键。问题总是在一个环境中可以复制,而在具有相同数据,Oracle版本和补丁程序的另一个环境中永远不能复制...



专家解答

是的 .... 没有某种排序子句 (row_number,或order by等),你可以安全地假设rownum给你一个 “伪随机” 的行集。

仅指定行数请求的rownum就会影响优化器,例如

SQL> create table t as
  2  select mod(rownum,2) col, d.* from dba_objects d;

Table created.

SQL>
SQL> create index ix on t ( col );

Index created.

SQL>
SQL> set autotrace traceonly explain
SQL> select * from t where col = 0 and rownum <= 1;

Execution Plan
----------------------------------------------------------
Plan hash value: 658510075

---------------------------------------------------------------------------------------------
| Id  | Operation                            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |      |     1 |   135 |     2   (0)| 00:00:01 |
|*  1 |  COUNT STOPKEY                       |      |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T    |     1 |   135 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | IX   |       |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=1)
   3 - access("COL"=0)

SQL> select * from t where col = 0 and rownum <= 100000;

Execution Plan
----------------------------------------------------------
Plan hash value: 508354683

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 39201 |  5168K|   432   (1)| 00:00:01 |
|*  1 |  COUNT STOPKEY     |      |       |       |            |          |
|*  2 |   TABLE ACCESS FULL| T    | 39201 |  5168K|   432   (1)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=100000)
   2 - filter("COL"=0)

复制


即使没有索引等,在一个简单的普通堆表上,从 * just * rownum查询回来的行基本上是在如何执行和调度操作系统的突发奇想,以及在插入行的时候发生了什么。下一个删除/插入可以移动行。因此,“随机”,因为rownum并不是真正的列-它是一个计数器: “我得到了符合我的标准的行,很酷,将1添加到rownum。”

这就是为什么

从表中选择 *,其中rownum = 2

永远不会有结果。它变成:

-我们吵了一架,
-它 “潜在” 的行数为1。
-等于2吗?
-不,拿到下一行
-它 “潜在” 的行数为1。
-等于2吗?
-它 “潜在” 的行数为1。
-等于2吗?
等等
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论