2009-09-11 23 views
7

私は、100万レコードのOracleデータベースを作成しています。私は最初の「Nを返すSQLクエリを記述しようとしています」ソートされたレコードは、特定の条件に基づいて、データベースから(100件のレコードを言う)。100万件のレコードを含むデータベースから最初の「N」レコードを選択する方法は?

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC 

を次にプログラムで最初のNレコードを選択します。

問題このアプローチである:五十万枚の レコードと「NAME BY ORDER」に

  • クエリ結果が すべてのレコードが降順にNAMEでソートされます。このソートは、多くの時間を取っている(ほとんど。 30〜40秒ds。 ORDER BYを省略すると、1秒しかかかりません)。
  • ソート後、最初のN(100)レコードだけが に興味があります。したがって、完全なレコードのソートは有用ではありません。

私の質問は以下のとおりです。

  1. それは クエリ自体に 'N' を指定することは可能ですか? (ソートはNレコードのみに適用され、クエリはより速くなります)。
  2. 問合せを改善して問合せを改善し、N個の要素のみを返すようにするより良い方法は、 時間です。

答えて

19

ランダムな行を100個見つけて並べ替える場合は、Lasse's solutionが正しいです。オプティマイザは、それがTOP-Nクエリで、使用することができるようになりますことを理解するであろう

SELECT * 
    FROM (SELECT * 
      FROM myTable 
     WHERE SIZE > 2000 ORDER BY NAME DESC) 
WHERE ROWNUM <= 100 

:私はあなたがこのようなクエリを構築するだろう他人を破棄しながら、名前でソートされた最初の100行をしたいと思うなら通りNAMEのインデックス。結果セット全体をソートする必要はなく、インデックスの最後から開始し、それを逆方向に読み込み、100行後に停止します。

また、元のクエリにヒントを追加して、オプティマイザが最初の行のみに興味があることを理解させることもできます。これはおそらく同様のアクセス・パスを生成します。

SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC 

編集:はOracleだけのROWNUMにソート前を帰属されているので動作しませんクエリにAND rownum <= 100を追加:これは、あなたがサブクエリを使用する必要が理由です。サブクエリがなければ、Oracleは100個のランダムな行を選択して並べ替えます。

+0

それは驚くべきことです! – Oliver

4

この追加:あなたのWHERE句に

AND rownum <= 100 

を。

しかし、これはあなたが求めていることをしません。

ランダムな行を100個選んで並べ替えてから返す場合は、まずORDER BYを指定せずにクエリを作成し、100行に制限してから選択してソートする必要があります。

この作品が、残念ながら私は、テストに使用可能なOracleサーバーがありませんでした:

SELECT * 
FROM (
    SELECT * 
    FROM myTable 
    WHERE SIZE > 2000 
     AND rownum <= 100 
    ) x 
ORDER BY NAME DESC 

をしかしそこに「ランダム」の部分は、あなたが私にして100行を与える」と言っているに注意してくださいSIZE> 2000、私は100 "を気にしません。

これは本当に必要なものですか?

いいえ、実際にはランダムな結果は得られません。つまり、サーバーにクエリを実行するたびに変更されますが、クエリオプティマイザが必要です。その表のデータのロードおよび索引統計が時間の経過とともに変化する場合、ある時点で前の照会と異なるデータを取得する可能性があります。

+0

お返事ありがとうございます。私のクエリはランダム100を取得することではありません。私は最初の100のソートされたレコードを取得したいです。例:レコードが1,5,8,2,14,3,6,7の場合そして、もし私が3つのレコードを必要とするなら、答えは(1,2,3) –

+2

となるでしょう。そして、最初にそれらを並べ替えることを望みます。そして百万行をソートするのに時間がかかるなら、それはあまり役に立ちません。ネットワーク経由ですべての行を取得するのではなく、ソートを実行するだけです。 –

+1

しかし、Oracleは、トップ100の結果を維持するのに十分なほどスマートです。次の行が100を超えている場合は、それを破棄します。このようにして、全体をソートする必要はありません。これはO(n log n)の代わりにO(n)です –

5

Thisは、ご使用のOracleのバージョンに応じて上位N行を選択する方法を示しています。以降のOracle 9iのより

、RANK()と DENSE_RANK()関数は、TOP N行を決定 に使用することができます。例:

からサル( ENAME、SAL、RANKを(SELECT))SAL DESC BY ORDER(OVERはEMP FROM をsal_rank)、 給料

SELECT ENAMEに基づいて上位10人の従業員を取得するWHERE sal_rank < = 10;

sal_dense_rank < = 10からSAL( ENAME、SAL、DENSE_RANK(選択))SAL DESC BY ORDER (OVERがEMP FROM をsal_dense_rank)、トップ10 給与

にSELECT ENAMEを行う従業員を選択;

2との差はhere

0

あなたの問題は、並べ替え、クエリが実行されるたびに行われているということです説明しました。索引を使用してソート操作を排除することができます。オプティマイザはソート操作を排除するために索引を使用できます。ソートされた列がNOT NULLと宣言されている場合

(カラムがNULL可能な場合は、(a)NOT NULL述語をクエリに追加するか、(b)関数ベースのインデックスを追加してそれに応じてORDER BY句を変更することによっても可能です)。

0

参考のため、Oracle 12cでは、FETCH句を使用してこのタスクを実行できます。この問題に関する例と参考資料のリンクはhereです。

関連する問題