100万件のレコードを含むデータベースから最初の「N」レコードを選択する方法
-
05-07-2019 - |
質問
100万件のレコードが含まれるOracleデータベースがあります。最初の 'N <!> quotを返すSQLクエリを作成しようとしています。特定の条件に基づいてデータベースからソートされたレコード(100レコードなど)。
SELECT *
FROM myTable
Where SIZE > 2000
ORDER BY NAME DESC
その後、プログラムで最初のN個のレコードを選択します。
このアプローチの問題は次のとおりです:
- クエリの結果は50万になります レコードと<!> quot; ORDER BY NAME <!> quot;原因 NAMEで降順でソートされるすべてのレコード。このソートには多くの時間がかかります。 (約30〜40秒。ORDERBYを省略すると、1秒しかかかりません。)
- ソート後、私が興味を持っている 最初のN(100)レコードのみ。したがって、完全なレコードのソートは有用ではありません。
私の質問:
- で「N」を指定することは可能ですか クエリ自体? (したがって、ソートはNレコードのみに適用され、クエリは高速になります。)
- 並べ替えるクエリを改善するSQLのより良い方法 N個の要素のみで、すぐに戻ります 時間。
解決
100個のランダムな行を見つけて後でソートすることが目的の場合、ラッセのソリューションは正しいです。私が思うに、最初の100行を名前でソートし、他の行を破棄したい場合は、次のようなクエリを作成します。
SELECT *
FROM (SELECT *
FROM myTable
WHERE SIZE > 2000 ORDER BY NAME DESC)
WHERE ROWNUM <= 100
オプティマイザーは、それがTOP-Nクエリであることを理解し、NAMEのインデックスを使用できるようになります。結果セット全体をソートする必要はありません。インデックスの最後から開始して逆方向に読み取り、100行後に停止します。
最初の行のみに関心があることをオプティマイザーに理解させるために、元のクエリにヒントを追加することもできます。これにより、おそらく同様のアクセスパスが生成されます。
SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC
編集:クエリにAND rownum <= 100
を追加するだけでは機能しません。これは、Oracleではrownumがソートの前であるため、サブクエリを使用する必要があるためです。サブクエリを使用しないと、Oracleは100個のランダムな行を選択してソートします。
他のヒント
これは、Oracleのバージョンに応じて上位N行を選択する方法を示しています。
Oracle 9i以降、RANK()および DENSE_RANK()関数を使用して、 TOP N行を決定します。例:
に基づいて上位10人の従業員を取得する 彼らの給料
SELECT ename、sal FROM(SELECT ename、sal、RANK()OVER(ORDER BY sal DESC)sal_rank FROM emp)WHERE sal_rank <!> lt; = 10;
トップ10を構成する従業員を選択します 給与
SELECT ename、sal FROM(SELECT ename、sal、DENSE_RANK()OVER(ORDER BY DESC)sal_dense_rank FROM emp)WHERE sal_dense_rank <!> lt; = 10;
この2つの違いは、こちら
これを追加:
AND rownum <= 100
WHERE句に。
しかし、これはあなたが求めていることをしません。
100個のランダムな行を選択し、並べ替えてから返す場合、最初にORDER BYなしでクエリを作成し、それを100行に制限してから選択して並べ替える必要があります。
このは動作しますが、残念ながらテストに使用できるOracleサーバーがありません:
SELECT *
FROM (
SELECT *
FROM myTable
WHERE SIZE > 2000
AND rownum <= 100
) x
ORDER BY NAME DESC
ただし、<!> quot; random <!> quot;に注意してください。そこにある、あなたは言っている<!> quot; SIZE <!> gt;で100行を与えてください。 2000、100 <!> quot;を気にしません。
それは本当にあなたが望むものですか?
いいえ、サーバーにクエリを送信するたびに結果が変わるという意味で、実際にはランダムな結果は得られませんが、クエリオプティマイザーに任されています。そのテーブルのデータロードとインデックスの統計が時間の経過とともに変化する場合、ある時点で、前のクエリとは異なるデータを取得する可能性があります。
問題は、クエリが実行されるたびにソートが実行されることです。ソートされた列がNOT NULLと宣言されている場合、インデックスを使用してソート操作を削除できます-オプティマイザーはインデックスを使用してソート操作を削除できます。
(列がNULL可能の場合、(a)クエリにNOT NULL述語を追加するか、(b)関数ベースのインデックスを追加し、それに応じてORDER BY句を変更することにより、引き続き可能です)
参照用に、Oracle 12cでは、このタスクはFETCH
句を使用して実行できます。 こちらをご覧ください。 この問題に関する例と追加の参照リンク。