10

これは私にとって終わりのないトピックであり、何かを見落としているのではないかと思っています。基本的に、私はアプリケーションで 2 種類の SQL ステートメントを使用します。

  1. 「フォールバック」制限のある通常のクエリ
  2. 並べ替えおよびページ化されたクエリ

ここで、数百万のレコードを持つテーブルに対するいくつかのクエリについて話し、数百万のレコードを持つさらに 5 つのテーブルに結合します。明らかに、それらすべてを取得することはほとんど望んでいません。そのため、上記の 2 つの方法でユーザー クエリを制限しています。

ケース 1は非常に単純です。ROWNUM追加のフィルターを追加するだけです。

WHERE ...
  AND ROWNUM < ?

Oracle の CBO はこのフィルターを実行計画に考慮し、おそらく操作を適用するため、これは非常に高速です(ヒントFIRST_ROWSによって強制されるものと同様です。/*+FIRST_ROWS*/

ただし、ケース 2LIMIT ... OFFSETは、他の RDBMS のように句がないため、Oracle では少し注意が必要です。したがって、「ビジネス」クエリを次のようにテクニカル ラッパーにネストします。

SELECT outer.* FROM (
  SELECT * FROM (
    SELECT inner.*, ROWNUM as RNUM, MAX(ROWNUM) OVER(PARTITION BY 1) as TOTAL_ROWS
    FROM (
      [... USER SORTED business query ...]
    ) inner
  ) 
  WHERE ROWNUM < ?
) outer
WHERE outer.RNUM > ?

TOTAL_ROWSフィールドは、すべてのデータを取得しなくてもページ数を知るために計算されることに注意してください。通常、このページング クエリは非常に満足のいくものです。しかし、ときどき (私が言ったように、500 万以上のレコードを照会するとき、おそらくインデックスなしの検索を含む)、これは 2 ~ 3 分間実行されます。

編集:ページングの前にソートを適用する必要があるため、潜在的なボトルネックを回避するのは簡単ではないことに注意してください!

LIMIT ... OFFSET私は疑問に思っています、Oracleを含むの最先端のシミュレーションですか、それとも疑似列の代わりにウィンドウ関数TOTAL_ROWSを使用するなど、設計により高速になるより良い解決策がありますか?ROW_NUMBER()ROWNUM

4

4 に答える 4

6

ケース2の主な問題は、多くの場合、最初のN行が返される前に、クエリ結果セット全体を取得してからソートする必要があることです。ただし、ORDER BY列にインデックスが付けられ、Oracleがそのインデックスを使用してソートを回避できる場合を除きます。複雑なクエリと大量のデータセットの場合、これには時間がかかることがあります。ただし、速度を向上させるためにできることがいくつかあるかもしれません。

  1. 内部SQLで関数が呼び出されないようにしてください。これらは、最初の20行を返すためだけに500万回呼び出される可能性があります。これらの関数呼び出しを外部クエリに移動できる場合は、呼び出される回数が少なくなります。
  2. FIRST_ROWS_nヒントを使用して、すべてのデータを返すことは決してないという事実を最適化するようにOracleを微調整します。

編集:

別の考え:現在、数千または数百万の行を返す可能性のあるレポートをユーザーに提示していますが、ユーザーがそれらすべてを実際にページングすることは決してありません。選択した日付範囲を3か月(またはそれ以上)に制限するなどして、より少量のデータを選択するように強制することはできませんか?

于 2011-05-17T15:44:16.437 に答える
3

時間がかかるクエリをトレースし、その実行計画を確認することをお勧めします。ほとんどの場合、パフォーマンスのボトルネックは TOTAL_ROWS の計算に起因します。Oracle はすべてのデータを読み取る必要があります。たとえ 1 行しかフェッチしない場合でも、これはすべての RDBMS がこのタイプのクエリで直面する一般的な問題です。これを回避する TOTAL_ROWS の実装はありません。

このタイプのクエリを高速化する抜本的な方法は、TOTAL_ROWS 計算を行わないことです。追加のページがあることを表示するだけです。あなたのユーザーは、52486 ページをページングできることを本当に知る必要がありますか? 見積もりで十分かもしれません。これは別の解決策で、たとえば Google 検索によって実装されています。ページ数を実際にカウントするのではなく、推定します。

正確で効率的な推定アルゴリズムを設計することは簡単ではないかもしれません。

于 2011-05-17T15:38:30.387 に答える
3

「LIMIT ... OFFSET」はほとんど構文糖衣です。クエリの見栄えが良くなるかもしれませんが、データセット全体を読み取って並べ替え、「50-60」行を取得する必要がある場合は、それが実行する必要がある作業です。

正しい順序でインデックスがあれば、それが役に立ちます。

于 2011-05-18T00:26:35.867 に答える
1

count() を試行して結果を同じクエリで返すよりも、2 つのクエリを実行した方がパフォーマンスが向上する場合があります。Oracle は、すべてのテーブルへの並べ替えや結合を行わずに count() に応答できる場合があります (宣言された外部キー制約に基づく結合テーブルの削除)。これは、アプリケーションで一般的に行うことです。パフォーマンスが重要なステートメントについては、Oracle よりも優れている場合があるため、正しいカウントを返すことがわかっている別のクエリを作成します。

または、パフォーマンスとデータの最新性との間でトレードオフを行うことができます。最初の 5 ページを戻すのは、最初のページを戻すのとほぼ同じ速さです。したがって、5 ページの結果を情報の有効期限とともに一時テーブルに保存することを検討できます。有効な場合は、一時テーブルから結果を取得します。期限切れのデータを定期的に削除するバックグラウンド タスクを配置します。

于 2011-05-18T08:59:03.837 に答える