バインド変数を使用すると、Oracleはを使用するように強制されます。動的パーティションプルーニング 静的パーティションプルーニング の代わりに 。この結果、入力変数に基づいて変更されるため、Oracleは解析時にどのパーティションにアクセスするかを認識しません。
これは、(バインド変数の代わりに)リテラル値を使用する場合、ローカルインデックスによってアクセスされるパーティションがわかっていることを意味します。したがって、count stopkey
パーティションを整理する前に、インデックスの出力に適用できます。
バインド変数を使用する場合、partition range iterator
アクセスしているパーティションを把握する必要があります。次に、操作間の最初の変数の値が2番目の変数(filter
)よりも実際に低いことを確認するためのチェックが行われます。 2番目の計画での運用)。
次のテストケースが示すように、これは簡単に再現できます。
create table tab (
x date,
y integer,
filler varchar2(100)
) partition by range(x) (
partition p1 values less than (date'2013-01-01'),
partition p2 values less than (date'2013-02-01'),
partition p3 values less than (date'2013-03-01'),
partition p4 values less than (date'2013-04-01'),
partition p5 values less than (date'2013-05-01'),
partition p6 values less than (date'2013-06-01')
);
insert into tab (x, y)
select add_months(trunc(sysdate, 'y'), mod(rownum, 5)), rownum, dbms_random.string('x', 50)
from dual
connect by level <= 1000;
create index i on tab(x desc, y desc) local;
exec dbms_stats.gather_table_stats(user, 'tab', cascade => true);
explain plan for
SELECT * FROM (
SELECT rowid FROM tab
where x between date'2013-01-01' and date'2013-02-02'
and y between 50 and 100
order by x desc, y desc
)
where rownum <= 5;
SELECT * FROM table(dbms_xplan.display(null, null, 'BASIC +ROWS +PARTITION'));
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Pstart| Pstop |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | |
| 1 | COUNT STOPKEY | | | | |
| 2 | VIEW | | 1 | | |
| 3 | SORT ORDER BY STOPKEY | | 1 | | |
| 4 | PARTITION RANGE ITERATOR| | 1 | 2 | 3 |
| 5 | COUNT STOPKEY | | | | |
| 6 | INDEX RANGE SCAN | I | 1 | 2 | 3 |
--------------------------------------------------------------------
explain plan for
SELECT * FROM (
SELECT rowid FROM tab
where x between to_date(:st, 'dd/mm/yyyy') and to_date(:en, 'dd/mm/yyyy')
and y between :a and :b
order by x desc, y desc
)
where rownum <= 5;
SELECT * FROM table(dbms_xplan.display(null, null, 'BASIC +ROWS +PARTITION'));
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Pstart| Pstop |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | |
| 1 | COUNT STOPKEY | | | | |
| 2 | VIEW | | 1 | | |
| 3 | SORT ORDER BY STOPKEY | | 1 | | |
| 4 | FILTER | | | | |
| 5 | PARTITION RANGE ITERATOR| | 1 | KEY | KEY |
| 6 | INDEX RANGE SCAN | I | 1 | KEY | KEY |
---------------------------------------------------------------------
あなたの例のように、2番目のクエリはパーティションをkey
にのみフィルタリングできます 最初の例のように正確なパーティションではなく、解析時に。
これは、リテラル値がバインド変数よりも優れたパフォーマンスを提供できるまれなケースの1つです。これがあなたにとって可能性があるかどうかを調査する必要があります。
最後に、各パーティションから20行が必要だと言います。スタンドとしてのクエリはこれを行いません。順序に従って最初の20行を返すだけです。 20行/パーティションの場合、次のような操作を行う必要があります。
select rd from (
select rowid rd,
row_number() over (partition by trx_id order by create_ts desc) rn
from OUT_SMS
where TRX_ID between ? and ?
and CREATE_TS between ? and ?
order by CREATE_TS DESC, TRX_ID DESC
) where rn <= 20
更新
count stopkey
を取得できない理由 filter
と関係があります 「悪い」計画の4行目の操作。上記の例を繰り返しますが、パーティション化を行わないと、これをより明確に確認できます。
これにより、次の計画が得られます。
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | COUNT STOPKEY | |
| 2 | VIEW | |
|* 3 | SORT ORDER BY STOPKEY| |
|* 4 | TABLE ACCESS FULL | TAB |
----------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=5)
3 - filter(ROWNUM<=5)
4 - filter("X">=TO_DATE(' 2013-01-01 00:00:00', 'syyyy-mm-dd
hh24:mi:ss') AND "X"<=TO_DATE(' 2013-02-02 00:00:00', 'syyyy-mm-dd
hh24:mi:ss') AND "Y">=50 AND "Y"<=100)
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | COUNT STOPKEY | |
| 2 | VIEW | |
|* 3 | SORT ORDER BY STOPKEY| |
|* 4 | FILTER | |
|* 5 | TABLE ACCESS FULL | TAB |
----------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=5)
3 - filter(ROWNUM<=5)
4 - filter(TO_NUMBER(:A)<=TO_NUMBER(:B) AND
TO_DATE(:ST,'dd/mm/yyyy')<=TO_DATE(:EN,'dd/mm/yyyy'))
5 - filter("Y">=TO_NUMBER(:A) AND "Y"<=TO_NUMBER(:B) AND
"X">=TO_DATE(:ST,'dd/mm/yyyy') AND "X"<=TO_DATE(:EN,'dd/mm/yyyy'))
ご覧のとおり、追加のfilter
があります sort order by stopkey
の前に表示されるバインド変数を使用する場合の操作 。これは、インデックスにアクセスした後に発生します。これは、変数の値がデータを返すことを許可することを確認しています(間にある最初の変数は、実際には2番目の変数よりも低い値を持っています)。オプティマイザは50が100未満(この場合)であることをすでに認識しているため、リテラルを使用する場合はこれは必要ありません。ただし、解析時に:aが:bよりも小さいかどうかはわかりません。
なぜこれが正確なのか私にはわかりません。これは、Oracleによる意図的な設計である可能性があります。つまり、変数に設定された値の行がゼロになるかどうかをストップキーでチェックしても意味がありません。または単に見落としているだけです。