Oracleでrow_numberを高速化するにはどうすればよいですか?
-
05-07-2019 - |
質問
次のようなSQLクエリがあります:
SELECT * FROM(
SELECT
...,
row_number() OVER(ORDER BY ID) rn
FROM
...
) WHERE rn between :start and :end
本質的に、物事を遅くしているのはORDER BY部分です。削除すると、EXPLAINコストは1桁(1000倍以上)低下します。私はこれを試しました:
SELECT
...
FROM
...
WHERE
rownum between :start and :end
しかし、これは正しい結果を与えません。これをスピードアップする簡単な方法はありますか?または、EXPLAINツールでもう少し時間を費やす必要がありますか?
解決
ROW_NUMBER
は、 Oracle
では非常に非効率的です。
パフォーマンスの詳細については、私のブログの記事を参照してください:
特定のクエリについては、 ROWNUM
に置き換えて、インデックスが使用されていることを確認することをお勧めします:
SELECT *
FROM (
SELECT /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */
t.*, ROWNUM AS rn
FROM table t
ORDER BY
column
)
WHERE rn >= :start
AND rownum <= :end - :start + 1
このクエリでは、 COUNT STOPKEY
また、 column
がNULL不可であることを確認するか、 WHERE column IS NOT NULL
条件を追加します。
それ以外の場合、インデックスを使用してすべての値を取得することはできません。
サブクエリなしでは ROWNUM BETWEEN:startおよび:end
を使用できないことに注意してください。
ROWNUM
は常に最後に割り当てられ、最後にチェックされます。これにより、 ROWNUM
が常に隙間なく順番に並べられます。
ROWNUM BETWEEN 10 and 20
を使用する場合、他のすべての条件を満たした最初の行が戻りの候補になり、一時的に ROWNUM = 1
が割り当てられ、失敗します ROWNUM BETWEEN 10 AND 20
のテスト。
次に、次の行が候補になり、 ROWNUM = 1
が割り当てられ、失敗などします。したがって、最終的に行はまったく返されません。
これは、 ROWNUM
をサブクエリに入れることで回避する必要があります。
他のヒント
ページネーションのクエリのように見えます。
このASKTOM記事から(ページの約90%):
何かで注文する必要がありますこれらのページネーションクエリに対して一意であるため、ROW_NUMBERは毎回行に確定的に割り当てられます。
また、クエリがほぼ同じではないため、一方のコストと他方のコストを比較することのメリットがわからない。
ORDER BY列にインデックスが作成されていますか?そうでない場合は、開始するのに適した場所です。
問題の一部は、「開始」から「終了」までのスパンの大きさと、それらが「生きている」場所です。 テーブルに100万行あり、行567,890から567,900が必要な場合は、テーブル全体を調べ、そのすべてをidでソートする必要があるという事実に耐えなければなりません。その行に含まれる行を特定します。
要するに、それは多くの作業です。そのため、オプティマイザーは高いコストをかけます。
また、インデックスが大いに役立つものでもありません。インデックスは順序を示しますが、せいぜいどこかで開始することができ、567,900番目のエントリに到達するまで読み続けます。
エンドユーザーに一度に10個のアイテムを表示する場合は、実際にDBから上位100個を取得し、アプリでその100個を10個のチャンクに分割する価値があります。
EXPLAIN PLANツールでより多くの時間を過ごします。 TABLE SCANが表示された場合は、クエリを変更する必要があります。
あなたのクエリは私にはほとんど意味がありません。 ROWIDを介したクエリは、トラブルを求めているようです。そのクエリには関係情報はありません。問題があるのは実際のクエリですか、それとも問題を説明するために作成した例ですか?