100万件のレコードを含むデータベースから最初の「N」レコードを選択する方法

StackOverflow https://stackoverflow.com/questions/1410048

質問

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)レコードのみ。したがって、完全なレコードのソートは有用ではありません。

私の質問:

  1. で「N」を指定することは可能ですか クエリ自体? (したがって、ソートはNレコードのみに適用され、クエリは高速になります。)
  2. 並べ替えるクエリを改善する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句を使用して実行できます。 こちらをご覧ください。 この問題に関する例と追加の参照リンク。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top