2013-02-26 31 views
84

SQL ServerからOracleにストアドプロシージャを変換して、製品との互換性を維持するのが苦労しています。Oracle ORDER BYとROWNUMを正しく使用する方法は?

私はタイムスタンプに基づいていくつかのテーブルの最新のレコードを返すクエリを、持っている:

SQL Serverの:

SELECT TOP 1 * 
FROM RACEWAY_INPUT_LABO 
ORDER BY t_stamp DESC 

=>それ意志は私の最新のレコードを返します

しかしオラクル:

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

(http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197 [ROWNUMと制限結果について]。 html) –

+4

あなたの最後のクエリで行ったことは正しいです。順序付けられたレコードのリストの1行目を選択します。単純にカプセル化を照会します。 – araknoid

+1

これはマニュアルに明記されています。http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns009.htm#i1006297 –

答えて

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() 'よりも速いので、他のものを上回ることは多くの要因に依存する。 –

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

ただ、例えば...私はこのユースケースにはお勧めし代替が最新の行を取得するにはMAX(t_stamp)を使用することです以下

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

これは2回下降して表示されています。私はこのソリューションがうまくいかないことを知っていますが、私は理由を知らない。なぜ誰かが説明してくれますか?ありがとうございました。 – JasonGabler

+1

@JasonGabler 't'は' ril'でなければなりません。彼らは3年後に受け入れられた答えからそのタイプミスをコピーしました。さらに、このコードは常に問題になっています。 「このような」の説明はないので、何も説明しません。 – philipxy

0

ようROWNUMの使用

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にはLIMITはありません。あなたは質問をしています。 – philipxy

関連する問題