Oracle ORDER BYおよびROWNUMを正しく使用する方法


126

ストアドプロシージャをSQL ServerからOracleに変換して、互換性のある製品にするのに苦労しています。

タイムスタンプに基づいて、いくつかのテーブルの最新のレコードを返すクエリがあります。

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

そしてそれは動作します。しかし、関係するテーブルに多くのレコードがある場合は特に、私にとっては恐ろしいハックのように思えます。

これを達成するための最良の方法は何ですか?



4
前回のクエリで行ったことは正しいです。レコードの順序付きリストの最初の行を選択します。単純にクエリのカプセル化。
アラノイド2013

1
:これは明らかに、マニュアルに記載されてdocs.oracle.com/cd/E11882_01/server.112/e26088/...
a_horse_with_no_name

5
@a_horse_with_no_nameつまり、この404エラーで明確に文書化されているということです。
anthonybrice 2015年

3
@anthonybrice:ありがとう。OracleはすべてのURLをマニュアルに変更しました。最新のリンクは次のとおり
E11882_01

回答:


120

whereステートメントが実行されます前にorder by。したがって、目的のクエリは「最初の行を取得してから、 t_stamp descでそれを並べ替える」と言っています。そしてそれはあなたが意図するものではありません。

サブクエリメソッドは、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」を返します。これを回避するには、列を個別にリストする必要があります。


40

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

3
ROWNUMより速くよりも可能性がありROW_NUMBER()そう、一方が他方の上にものを使用する必要があるかどうかは、多くの要因に依存します。
David Faber

誤って行われた反対投票に対する謝罪!残念ながら、今は取り戻すことができません。
アサフード、

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

私のコーディングパターンの設定(おそらく)-信頼性が高く、通常、並べ替えられたリストから最初の行を選択するのと同じかそれ以上に優れています-また、意図がより明確に読み取れます。
お役に立てれば ...

SQLer


3
Oracleには制限はありません。あなたは質問を懇願しています。
philipxy 2017

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の実際のクエリ内で制限されているかどうかは不明です。わからない。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.