2013-02-26 46 views
84

我很難將存儲過程從SQL Server轉換爲Oracle,以使我們的產品與其兼容。如何正確使用Oracle ORDER BY和ROWNUM?

我有一個基於時間戳的查詢返回最近的一些表的記錄,:

SQL服務器:

SELECT TOP 1 * 
FROM RACEWAY_INPUT_LABO 
ORDER BY t_stamp DESC 

=>將返回我最近的記錄

但是Oracle:

SELECT * 
FROM raceway_input_labo 
WHERE rownum <= 1 
ORDER BY t_stamp DESC 

=>無論ORDER BY聲明如何,這將返回最舊的記錄(可能取決於索引)!

我封裝的Oracle查詢這種方式來滿足我的要求:

SELECT * 
FROM 
    (SELECT * 
    FROM raceway_input_labo 
    ORDER BY t_stamp DESC) 
WHERE rownum <= 1 

和它的作品。但是對我來說這聽起來像是一個可怕的黑客攻擊,特別是如果我在涉及的表中有很多記錄。

達到此目的的最佳方法是什麼?

+0

[開ROWNUM和限制結果](http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197。 html) – 2013-02-26 14:41:24

+4

您在上次查詢中所做的操作是正確的。您選擇記錄的有序列表的第一行。簡單查詢封裝。 – araknoid 2013-02-26 14:49:03

+1

在手冊中明確記錄了這一點:http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns009.htm#i1006297 – 2013-02-26 15:06:42

回答

84

where聲明得到執行之前order by。因此,您所需的查詢是說「採取第一行,然後通過t_stampdesc」訂購它。這不是你想要的。

子查詢方法是在Oracle中執行此操作的正確方法。

如果你想在這兩個服務器協同工作的一個版本,你可以使用:

select ril.* 
from (select ril.*, row_number() over (order by t_stamp desc) as seqnum 
     from raceway_input_labo ril 
    ) ril 
where seqnum = 1 

*將在最後一列返回「1」。您需要單獨列出列以避免這種情況。

19

改爲使用ROW_NUMBER()ROWNUM是一個僞列,ROW_NUMBER()是一個函數。您可以閱讀它們之間的差異並查看以下查詢的輸出差異:

SELECT * FROM (SELECT rownum, deptno, ename 
      FROM scott.emp 
     ORDER BY deptno 
     ) 
WHERE rownum <= 3 
/

ROWNUM DEPTNO ENAME 
--------------------------- 
7  10 CLARK 
14  10 MILLER 
9  10 KING 


SELECT * FROM 
(
    SELECT deptno, ename 
     , ROW_NUMBER() OVER (ORDER BY deptno) rno 
    FROM scott.emp 
ORDER BY deptno 
) 
WHERE rno <= 3 
/

DEPTNO ENAME RNO 
------------------------- 
10 CLARK  1 
10 MILLER  2 
10 KING   3 
+2

'ROWNUM'可能比'ROW_NUMBER()'快,所以不管是否應該使用一個另一方面取決於許多因素。 – 2015-02-06 22:26:38

0

在上面的註釋中記錄了一些與此相關的設計問題。在Oracle中有一個小故事,當你有大的表和/或具有相同列名的表時(你不想明確地將它們全部輸出並全部重命名),你需要手動限制結果。簡單的解決方案是找出你的斷點並限制你的查詢。或者,如果您沒有衝突的列名稱約束,則也可以在內部查詢中執行此操作。 例如

WHERE 
m_api_log.created_date BETWEEN TO_DATE ('10/23/2015 05:00', 'MM/DD/YYYY HH24:MI') AND TO_DATE ('10/30/2015 23:59', 'MM/DD/YYYY HH24:MI') 

會大幅減少結果。然後你可以ORDER BY或甚至做外部查詢來限制行。

此外,我認爲TOAD有限制行的功能;但是,不確定在Oracle的實際查詢中是否存在限制。不確定。

-2

只需使用ROWNUM像下面

select * 
from (select t.* 
     from raceway_input_labo ril 
     order by t_stamp desc 
    )  
where rownum = 1 
+0

是看到這是downvoted兩次。我知道這個解決方案工作不正常,但我不知道爲什麼。請有人解釋爲什麼?謝謝。 – JasonGabler 2017-07-12 17:23:55

+1

@JasonGabler't'應該是'ril'。他們在3年後從接受的答案中複製了這個錯字。而且這個代碼一直存在於這個問題中。沒有解釋「像這樣」,所以它沒有解釋任何東西。 – philipxy 2017-09-30 01:45:48

0

一種替代我會在這個用例建議是使用MAX(t_stamp),以獲得最新的行...例如

select t.* from raceway_input_labo t 
where t.t_stamp = (select max(t_stamp) from raceway_input_labo) 
limit 1 

我的編碼模式的偏好(也許) - 可靠,通常在執行或高於試圖從分類列表中選擇第1行更好 - 也是其目的是更明確的可讀性。
希望這有助於...

SQLer

+0

Oracle中沒有限制。你在乞求這個問題。 – philipxy 2017-09-30 02:23:11