私は自分のデータベース(11g)のテーブルからページ分割された結果を取得する作業をしています。機能するクエリがあります(つまり、結果は正しいです)が、期待どおりに機能せず、効率を向上させようとしています(そのクエリで1秒あたり最大60回の呼び出し)。
最初に、私は Oracleのページ数をカウントする効果的な方法は何ですか? を読み、記事も指摘しましたが、残念ながら、提示されたクエリの実行計画についてはまったく触れていません。 (私は彼らが月曜日に来るのを見ることになるでしょう)。
これが私が思いついたクエリです(テーブルは日付範囲であるpart_code
で分割されています):
select <all-columns> from (
select rownum as rnum, <all-columns> from (
select /*+index (my_table index)*/ <all-columns> from my_table
where part_code in (?, ?, ?)
and date between ? and ?
and type in (?, ?, ?)
and func_col1 = ?
/*potentially: and func_col2 = ? and func_col3 = ? ... */
order by date, type, id
)
) where rnum between M and N; /* N-M ~= 30 */
注:ほとんどのクエリは単一のfunc_xxx
フィルターを使用して実行され、Mは小さいと思いますが、保証はありません。理論的には、Mは9999まで可能です。
注:合計で約72パーティションですが、最大で約39のみがアクティブで、func_col1
の値は約300,000です(ただし、一部は50,000行、一部は1行です)、type
、id
の最大値は10です。 (シーケンスによって生成されます)。
これは機能しますが、実行計画には厄介な驚きがあります。2番目のクエリ(rownum as rnum
を使用)は完全に実行され、ページ分割が始まる前にDBから最大50,000行をフェッチします。最大50,000行をフェッチします。 DBからそれらの約30を返すには、特に非効率的です。
実行計画では、これは次のように表示されます。
View
\_ Filter (rnum)
\_ View <= here comes the trouble
\_ Sort
\_ ...
必要に応じてテーブルにインデックスを作成し、既存のパーティションインデックス(part_code, func_col1, date, type, id
)を変換できます。インデックスは、私のorder by
句で要求される順序とまったく同じです。しかし、それは十分ではないように思われます(そして、order by句を削除しても、とにかく近づくことはありません)。
外部クエリがより多くのデータを必要とする(つまり、移動する)ビューの「具体化」を防止し、Oracleがオンザフライで作成する方法はありますか?内部クエリの遅延評価へ)?
実際のソート操作を回避し、できるだけ早く「停止」する計画を目指すべきだと思います。
並べ替え(および内部ビューの "具体化")を回避するには、並べ替え順序がインデックス列と正確に一致するか、またはwhere句がすべての先行列でのみ厳密に等しい必要があります。それ以外の場合は、サブセットをソートする必要があり、内部ビュー全体を評価する必要があります。
以下は、架空のfoo
テーブルの例です。
create table foo (a number, b number, c varchar(100));
-- fill with dummy data
exec dbms_stats.gather_table_stats(ownname => user, tabname => 'FOO');
create index foo_ix on foo(a, b, c);
あなたの選択に最も近い単純なもの:
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by c
) i
) where r between 5 and 10;
説明計画は良くありません:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 163 | 14833 | 5 (20)| 00:00:01 |
|* 1 | VIEW | | 163 | 14833 | 5 (20)| 00:00:01 |
| 2 | COUNT | | | | | |
| 3 | VIEW | | 163 | 12714 | 5 (20)| 00:00:01 |
| 4 | SORT ORDER BY | | 163 | 9291 | 5 (20)| 00:00:01 |
| 5 | INLIST ITERATOR | | | | | |
|* 6 | INDEX RANGE SCAN| FOO_IX | 163 | 9291 | 4 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R"<=10 AND "R">=5)
6 - access(("A"=3 OR "A"=4) AND "B"=3)
カウントは遅すぎ、この場合、実際には(物事を「止める」というわけではありません)。
インデックス列を注文に追加します(要件を満たしていない可能性があります)。
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by a, b, c
) i
) where r between 5 and 10;
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 163 | 14833 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 163 | 14833 | 4 (0)| 00:00:01 |
| 2 | COUNT | | | | | |
| 3 | VIEW | | 163 | 12714 | 4 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
|* 5 | INDEX RANGE SCAN| FOO_IX | 163 | 9291 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R"<=10 AND "R">=5)
5 - access(("A"=3 OR "A"=4) AND "B"=3)
少なくとも、私たちはその種類を取り除きました。さあ、試してみて、「やめて」:
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by a, b, c
) i where rownum < 10
) where r > 5;
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 819 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 9 | 819 | 4 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 9 | 702 | 4 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
|* 5 | INDEX RANGE SCAN| FOO_IX | 9 | 513 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R">5)
2 - filter(ROWNUM<10)
5 - access(("A"=3 OR "A"=4) AND "B"=3)
これはあなたのクエリには十分かもしれません:count stopkey
(rownum <
マジック、私が見たものではbetween
でキックしない)と「行」列に注意してください-内部スキャンはしませんN
が見つかった後、さらに行をフェッチすることを気にする必要があります。
上記を使用しても、テーブルからN
行を読み取ることができます。
ifすべての検索条件にインデックスを付けることを制限できます。上記のフィルタリングを実行しますが、すべての列ではなく各一致からROWID
のみを取得し、ROWID
でテーブルにアクセスします。
select * from foo where rowid in (
select rid from (
select rownum r, i.* from (
select rowid rid
from foo
where a in (3,4) and b = 3
order by a, b, c
) i where rownum < 10
) where r > 5
);
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 78 | 6 (17)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 78 | 6 (17)| 00:00:01 |
| 2 | VIEW | VW_NSO_1 | 9 | 108 | 4 (0)| 00:00:01 |
| 3 | HASH UNIQUE | | 1 | 225 | | |
|* 4 | VIEW | | 9 | 225 | 4 (0)| 00:00:01 |
|* 5 | COUNT STOPKEY | | | | | |
| 6 | VIEW | | 9 | 108 | 4 (0)| 00:00:01 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | FOO_IX | 9 | 594 | 4 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY USER ROWID| FOO | 1 | 66 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("R">5)
5 - filter(ROWNUM<10)
8 - access(("A"=3 OR "A"=4) AND "B"=3)
このしないは、検索フィールドのいずれかがインデックスにない場合に機能します。そして、実際のデータと通常の検索基準でこれをトレースして、実質的に違いがあるかどうかを確認してください。特に、M
の低い値(おそらく、最速にしたい場合)またはN-M
。