2016-04-20 19 views
0

Oracle的ROWNUM應用於ORDER BY之前。爲了根據排序列放ROWNUM,在所有文檔和文本中提出以下子查詢。ORDER BY子查詢和ROWNUM違背關係哲學?

select * 
from (
    select * 
    from table 
    order by price 
) 
where rownum <= 7 

這讓我感到困惑。據我所知,輸入到FROM的表格是關係型的,因此沒有存儲訂單,這意味着當通過FROM查看時,子查詢中的訂單不被遵守。

我不記得確切的情況,但「ORDER BY這個事實在外層查詢中沒有任何影響」我讀了不止一次。例子是在線子查詢,子查詢INSERT,PARTITION子句,等。例如ORDER BY

OVER (PARTITION BY name ORDER BY salary)

工資的預定將不會在外部查詢得到尊重,如果我們想在排序工資外部查詢輸出,需要在外部查詢中添加另一個ORDER BY

從大家的一些見解,爲什麼關係屬性不被尊重在這裏和順序存儲在子查詢?

+0

ROWNUM和ORDER BY都不是關係概念。 –

回答

8

在此上下文中的ORDER BY實際上是用於在(邏輯上)無序的一組行上生成「有序」行號的Oracle專有語法。這是我的意見,但相當於ISO標準的SQL ROW_NUMBER()函數(也適用於Oracle)的設計不良的特徵可以更清楚發生了什麼:

select * 
from (
    select ROW_NUMBER() OVER (ORDER BY price) rn, * 
    from table 
) t 
where rn <= 7; 

在這個例子中ORDER BY去它那裏有邏輯上屬於:作爲派生行號屬性的規範的一部分。這比Oracle的版本更強大,因爲您可以在同一結果中指定幾個不同的排序來定義不同的行號。此查詢返回的行的實際排序是未定義的。我相信在你的Oracle特定版本的查詢中也是如此,因爲當你以這種方式使用ORDER BY時不能保證排序。

值得一提的是,Oracle不是一個關係數據庫管理系統。與其他SQL DBMS一樣,Oracle在一些基本方面背離了關係模型。產品中存在隱式排序和DISTINCT之類的功能,這正是由於數據的SQL模型的非關係性質以及因此需要解決具有重複行的無密鑰表而造成的。

+1

[ROWNUM的Oracle文檔](http://docs.oracle.com/cd/E11882_01/server.112/e41084/pseudocolumns009.htm#SQLRF00255)甚至說ROW_NUMBER()更好* 8-) –

+0

@Kenny:優秀的答案;寫作主要是爲了告訴你(肯尼)我對這個問題的感受完全一樣。據說,無論如何我決定學習Oracle(儘管現在只是爲了我自己的滿意,我不以任何方式在現場工作)。我個人總是使用sqlvogel顯示的row_number(),這正是你提出的原因。當你更多地使用Oracle時,你會發現很多令人不安的原因;對我來說最大的問題是它們對NULL字符串的處理:零長度字符串與NULL相同,反之亦然。也許甲骨文至少會修復這個問題...... – mathguy

+0

@mathguy ['VARCHAR' vs'VARCHAR2'](http://stackoverflow.com/questions/1171196/what-is-the-difference-between-varchar-和-varchar2) – MT0

3

毫不奇怪,Oracle將這視爲一種特殊情況。你可以從執行計劃中看到。隨着天真的(不正確的/不確定的),有時作物了限制的版本,你會得到SORT ORDER BYCOUNT STOPKEY操作:

select * 
from my_table 
where rownum <= 7 
order by price; 

--------------------------------------------------------------------------------   
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  |   
--------------------------------------------------------------------------------   
| 0 | SELECT STATEMENT |   |  1 | 13 |  3 (34)| 00:00:01 |   
| 1 | SORT ORDER BY  |   |  1 | 13 |  3 (34)| 00:00:01 |   
|* 2 | COUNT STOPKEY  |   |  |  |   |   |   
| 3 | TABLE ACCESS FULL| MY_TABLE |  1 | 13 |  2 (0)| 00:00:01 |   
--------------------------------------------------------------------------------   

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

    2 - filter(ROWNUM<=7)                 

如果只是用一個有序的子查詢,沒有限制,你只能得到SORT ORDER BY操作:

select * 
from (
    select * 
    from my_table 
    order by price 
); 

-------------------------------------------------------------------------------   
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  |   
-------------------------------------------------------------------------------   
| 0 | SELECT STATEMENT |   |  1 | 13 |  3 (34)| 00:00:01 |   
| 1 | SORT ORDER BY  |   |  1 | 13 |  3 (34)| 00:00:01 |   
| 2 | TABLE ACCESS FULL| MY_TABLE |  1 | 13 |  2 (0)| 00:00:01 |   
-------------------------------------------------------------------------------   

與通常的子查詢/ ROWNUM構造你得到的東西不同,

select * 
from (
    select * 
    from my_table 
    order by price 
) 
where rownum <= 7; 

------------------------------------------------------------------------------------  
| Id | Operation    | Name  | Rows | Bytes | Cost (%CPU)| Time  |  
------------------------------------------------------------------------------------  
| 0 | SELECT STATEMENT  |   |  1 | 13 |  3 (34)| 00:00:01 |  
|* 1 | COUNT STOPKEY   |   |  |  |   |   |  
| 2 | VIEW     |   |  1 | 13 |  3 (34)| 00:00:01 |  
|* 3 | SORT ORDER BY STOPKEY|   |  1 | 13 |  3 (34)| 00:00:01 |  
| 4 |  TABLE ACCESS FULL | MY_TABLE |  1 | 13 |  2 (0)| 00:00:01 |  
------------------------------------------------------------------------------------  

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

    1 - filter(ROWNUM<=7)                 
    3 - filter(ROWNUM<=7)                 

對於外部查詢,COUNT STOPKEY操作仍然存在,但內部查詢(內聯視圖或派生表)現在具有SORT ORDER BY STOPKEY而不是簡單的SORT ORDER BY。這一切都隱藏在內部,所以我猜測,但它看起來像停止鍵 - 即行號限制 - 被推入子查詢處理,所以實際上子查詢可能只有七行無論如何 - 儘管該計劃的ROWS價值沒有反映(但是然後你得到相同的計劃與不同的限制),並且它仍然認爲需要單獨應用COUNT STOPKEY操作。

湯姆凱特覆蓋類的理由in an Oracle Magazine article,談到「頂ñ查詢處理與ROWNUM」時(強調):

有兩種方法可以解決這個:
- 讓客戶機應用程序運行該查詢並獲取前N行。
- 使用查詢作爲內嵌視圖,並使用ROWNUM限制結果,如在SELECT * FROM(your_query_here)WHERE ROWNUM < = N.

第二種方法是通過遠遠優於第一,對於兩個原因。這兩個原因中較小的一個是,客戶端需要較少的工作,因爲數據庫負責限制結果集。更重要的原因是特殊處理數據庫可以做到只給你最上面的N行。使用前N個查詢意味着您已向數據庫提供額外信息。你已經告訴過它,「我只對獲得N行感興趣,我永遠不會考慮其餘的。」現在,直到你考慮分類之前,這聽起來不會太驚人 - 分類如何工作以及服務器需要做什麼。

...然後繼續概述它實際在做什麼,而不是我可以更權威。

有趣的是,我不認爲最終結果集的順序實際上是有保證的;它似乎總是工作,但可以證明你仍然應該在外部查詢上也有一個ORDER BY以使其完成。看起來這個訂單並不真正存儲在子查詢中,它恰好就是這樣生成的。 (我非常懷疑它會改變,因爲它會破壞太多的東西;這最終看起來類似於表集合表達式,它似乎總是保留它的順序 - 破壞會阻止dbms_xplan工作,但我確信有其他例子。)

只是作比較,這是什麼ROW_NUMBER()相當於做:

select * 
from (
    select ROW_NUMBER() OVER (ORDER BY price) rn, my_table.* 
    from my_table 
) t 
where rn <= 7; 

-------------------------------------------------------------------------------------  
| Id | Operation    | Name  | Rows | Bytes | Cost (%CPU)| Time  |  
-------------------------------------------------------------------------------------  
| 0 | SELECT STATEMENT   |   |  2 | 52 |  4 (25)| 00:00:01 |  
|* 1 | VIEW     |   |  2 | 52 |  4 (25)| 00:00:01 |  
|* 2 | WINDOW SORT PUSHED RANK|   |  2 | 26 |  4 (25)| 00:00:01 |  
| 3 | TABLE ACCESS FULL  | MY_TABLE |  2 | 26 |  3 (0)| 00:00:01 |  
-------------------------------------------------------------------------------------  

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

    1 - filter("RN"<=7)                  
    2 - filter(ROW_NUMBER() OVER (ORDER BY "PRICE")<=7)         
+0

感謝您的投入Alex。一個讓我想起來的想法是:內部查詢_(SELECT *)_實際上是否包含真實表列旁邊的僞列_ROWNUM_和_ROWID_,並且它們被傳遞給外部查詢,因此訂單是保留的?或者ROWNUM只有在提到時纔會採取行動? – Kenny

+0

@Kenny - 內部查詢有它自己的'ROWNUM' psuedocolumn,但正如你注意到的那樣,是在應用order-by之前生成的。你可以看看它,甚至給它一個別名,以便外部查詢可以看到它。但它不一定(在邏輯上)與外部查詢中的'ROWNUM'具有相同的值。例如,嘗試:'select t。*,rownum from(選擇my_table。*,rownum作爲rn from my_table order by price)t其中rownum <= 7;' –

0

添加到sqlvogel的很好的答案:

「據我瞭解,桌子下,從是關係」

不,輸入FROM的表不是關係型的。它不是關係型的,因爲「表格輸入」是表格和表格不是關係。 SQL中無數的怪癖和怪異最終都歸結爲一個簡單的事實:SQL中的核心構建磚塊是表格,而表格不是關係。總結區別:

表可以包含重複行,關係不能。 (因此,SQL提供了包代數,而不是關係代數。另一個結果是,SQL甚至不可能爲其最基本的建築磚塊定義相等比較!!!如果您比較表,您如何比較表格可能需要處理重複的行嗎?)

表可以包含未命名的列,關係不能。 SELECT X + Y FROM ...結果,SQL被強制爲「序列位置的列標識」,因此,你會遇到各種各樣的怪癖。在SELECT A,B FROM ... UNION SELECT B,A FROM ...

表可以包含重複的列名,關係不能。表中的A.ID和B.ID是而不是不同的列名稱。點之前的部分不是名稱的一部分,它是一個「範圍標識符」,並且一旦你在「出現/引入的SELECT之外」,該範圍標識符就會「消失」。您可以使用嵌套SELECT:SELECT A.ID FROM(SELECT A.ID,B.ID FROM ...)。它不會工作(除非您的特定實現偏離標準才能使其工作)。

各種SQL結構給人留下的印象是表格確實有排序。很明顯,ORDER BY子句,還有GROUP BY子句(只能通過引入相當不友好的「行間組合在一起的中間表」的概念)來工作。關係根本就不是這樣。

表可以包含NULL,關係不能。這一個被打死了。

應該還有一些,但我不記得他們從帽子的頂端。