これは私にとって終わりのないトピックであり、何かを見落としているのではないかと思っています。基本的に、私はアプリケーションで 2 種類の SQL ステートメントを使用します。
- 「フォールバック」制限のある通常のクエリ
- 並べ替えおよびページ化されたクエリ
ここで、数百万のレコードを持つテーブルに対するいくつかのクエリについて話し、数百万のレコードを持つさらに 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