ORDER BY子查询和ROWNUM违背了关系哲学?

Ken*_*nny 1 oracle sql-order-by relational-database oracle11g rownum

Oracle ROWNUM之前已经应用过了ORDER BY.为了ROWNUM根据排序列放置,在所有文档和文本中提出以下子查询.

select *
from (
  select *
  from table
  order by price
)
where rownum <= 7
Run Code Online (Sandbox Code Playgroud)

这让我很烦恼.据我所知,表输入FROM是关系的,因此没有存储顺序,这意味着子查询中的顺序在被看到时不被尊重FROM.

我不记得确切的情况,但这个" ORDER BY在外部查询中没有任何影响"的事实我不止一次阅读过.示例是内联子查询INSERT,ORDER BYPARTITION子句的子查询等.例如

OVER (PARTITION BY name ORDER BY salary)

外部查询中不会遵守工资订单,如果我们希望在外部查询输出中对工资进行排序,则ORDER BY需要在外部查询中添加另一个工资.

每个人都有一些关于为什么关系属性在这里不受尊重和订单的见解存储在子查询中?

nvo*_*gel 9

此上下文中的ORDER BY实际上是Oracle的专有语法,用于在(逻辑)无序行集上生成"有序"行号.在我看来,这是一个设计不佳的功能,但等效的ISO标准SQL ROW_NUMBER()函数(在Oracle中也有效)可能会使发生的事情变得更加清晰:

select *
from (
  select ROW_NUMBER() OVER (ORDER BY price) rn, *
  from table
) t
where rn <= 7;
Run Code Online (Sandbox Code Playgroud)

在此示例中,ORDER BY位于逻辑上更属于的位置:作为派生行号属性的规范的一部分.这比Oracle的版本更强大,因为您可以指定在同一结果中定义不同行号的几个不同的排序.此查询返回的行的实际排序是未定义的.我相信在特定于Oracle的查询版本中也是如此,因为当您以这种方式使用ORDER BY时,无法保证订购.

值得记住的是Oracle不是关系型DBMS.与其他SQL DBMS一样,Oracle在某些基本方面偏离了关系模型.产品中存在隐式排序和DISTINCT等功能,因为数据的SQL模型具有非关系性,因此需要解决具有重复行的无密钥表.


Ale*_*ole 5

毫不奇怪,Oracle 将此视为一个特例。从执行计划中就可以看出这一点。对于有时会出现的极限(不正确/不确定)版本,您将得到SORT ORDER BY以下COUNT STOPKEY操作:

\n\n
select *\nfrom my_table\nwhere rownum <= 7\norder by price;\n\n--------------------------------------------------------------------------------          \n| Id  | Operation           | Name     | Rows  | Bytes | Cost (%CPU)| Time     |          \n--------------------------------------------------------------------------------          \n|   0 | SELECT STATEMENT    |          |     1 |    13 |     3  (34)| 00:00:01 |          \n|   1 |  SORT ORDER BY      |          |     1 |    13 |     3  (34)| 00:00:01 |          \n|*  2 |   COUNT STOPKEY     |          |       |       |            |          |          \n|   3 |    TABLE ACCESS FULL| MY_TABLE |     1 |    13 |     2   (0)| 00:00:01 |          \n--------------------------------------------------------------------------------          \n\nPredicate Information (identified by operation id):                                       \n---------------------------------------------------                                       \n\n   2 - filter(ROWNUM<=7)                                                                  \n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你只是使用有序子查询,没有限制,你只会得到这样的SORT ORDER BY操作:

\n\n
select *\nfrom (\n  select *\n  from my_table\n  order by price\n);\n\n-------------------------------------------------------------------------------           \n| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |           \n-------------------------------------------------------------------------------           \n|   0 | SELECT STATEMENT   |          |     1 |    13 |     3  (34)| 00:00:01 |           \n|   1 |  SORT ORDER BY     |          |     1 |    13 |     3  (34)| 00:00:01 |           \n|   2 |   TABLE ACCESS FULL| MY_TABLE |     1 |    13 |     2   (0)| 00:00:01 |           \n-------------------------------------------------------------------------------           \n
Run Code Online (Sandbox Code Playgroud)\n\n

使用通常的子查询/ROWNUM构造你会得到不同的东西,

\n\n
select *\nfrom (\n  select *\n  from my_table\n  order by price\n)\nwhere rownum <= 7;\n\n------------------------------------------------------------------------------------      \n| Id  | Operation               | Name     | Rows  | Bytes | Cost (%CPU)| Time     |      \n------------------------------------------------------------------------------------      \n|   0 | SELECT STATEMENT        |          |     1 |    13 |     3  (34)| 00:00:01 |      \n|*  1 |  COUNT STOPKEY          |          |       |       |            |          |      \n|   2 |   VIEW                  |          |     1 |    13 |     3  (34)| 00:00:01 |      \n|*  3 |    SORT ORDER BY STOPKEY|          |     1 |    13 |     3  (34)| 00:00:01 |      \n|   4 |     TABLE ACCESS FULL   | MY_TABLE |     1 |    13 |     2   (0)| 00:00:01 |      \n------------------------------------------------------------------------------------      \n\nPredicate Information (identified by operation id):                                       \n---------------------------------------------------                                       \n\n   1 - filter(ROWNUM<=7)                                                                  \n   3 - filter(ROWNUM<=7)                                                                  \n
Run Code Online (Sandbox Code Playgroud)\n\n

外部查询的操作COUNT STOPKEY仍然存在,但内部查询(内联视图或派生表)现在有一个SORT ORDER BY STOPKEY而不是简单的SORT ORDER BY. 这一切都隐藏在内部,所以我推测,但看起来停止键 - 即行数限制 - 被推入子查询处理中,因此实际上子查询最终可能只会有七行- 尽管计划的ROWS值没有反映这一点(但随后您会得到具有不同限制的相同计划),并且仍然感觉需要COUNT STOPKEY单独应用该操作。

\n\n

Tom Kyte在 Oracle 杂志文章中阐述了类似的观点谈到“使用 ROWNUM 进行 Top-N 查询处理”时也谈到了类似的观点(强调是后加的):

\n\n
\n

有两种方法可以解决此问题:
\n - 让客户端应用程序运行该查询并仅获取前 N 行。
\n - 使用该查询作为内联视图,并使用 ROWNUM 来限制结果,如 SELECT * FROM ( your_query_here ) WHERE ROWNUM <= N 中所示。

\n\n

第二种方法远远优于第一种,原因有二。这两个原因中较小的一个是它需要客户端的工作较少,因为数据库负责限制结果集。更重要的原因是数据库可以进行特殊处理,只提供前 N 行。使用 top-N 查询意味着您已经向数据库提供了额外的信息。你已经告诉它,“我只对获取 N 行感兴趣;我永远不会考虑其余的。” 现在,这听起来并不算太惊天动地,直到您考虑排序\xe2\x80\x94 排序如何工作以及服务器需要做什么。

\n
\n\n

...然后继续概述它实际上在做什么,比我更权威。

\n\n

有趣的是,我认为最终结果集的顺序实际上并没有得到保证;它似乎总是有效,但可以说您仍然应该ORDER BY在外部查询上进行操作以使其完整。看起来订单并没有真正存储在子查询中,它只是碰巧是这样生成的。(我非常怀疑这是否会改变,因为它会破坏太多东西;这最终看起来类似于表集合表达式,它似乎也总是保留其顺序 - 破坏会停止工作dbms_xplan。我确信有其他例子。)

\n\n

只是为了比较,这就是ROW_NUMBER()等价物的作用:

\n\n
select *\nfrom (\n  select ROW_NUMBER() OVER (ORDER BY price) rn, my_table.*\n  from my_table\n) t\nwhere rn <= 7;\n\n-------------------------------------------------------------------------------------     \n| Id  | Operation                | Name     | Rows  | Bytes | Cost (%CPU)| Time     |     \n-------------------------------------------------------------------------------------     \n|   0 | SELECT STATEMENT         |          |     2 |    52 |     4  (25)| 00:00:01 |     \n|*  1 |  VIEW                    |          |     2 |    52 |     4  (25)| 00:00:01 |     \n|*  2 |   WINDOW SORT PUSHED RANK|          |     2 |    26 |     4  (25)| 00:00:01 |     \n|   3 |    TABLE ACCESS FULL     | MY_TABLE |     2 |    26 |     3   (0)| 00:00:01 |     \n-------------------------------------------------------------------------------------     \n\nPredicate Information (identified by operation id):                                       \n---------------------------------------------------                                       \n\n   1 - filter("RN"<=7)                                                                    \n   2 - filter(ROW_NUMBER() OVER ( ORDER BY "PRICE")<=7)                                   \n
Run Code Online (Sandbox Code Playgroud)\n