Oracle 11gへのアップグレード後にOracleクエリを最適化するのに問題があり、この問題が私を少し怒らせ始めています。
簡単なテストケースを作成した後、より多くの情報が得られたため、この質問は完全に編集されていることに注意してください。元の質問はここにあります:https ://stackoverflow.com/revisions/12304320/1 。
この問題は、2つのテーブルを結合するときに、そのうちの1つがbetween
日付列に条件を持っている場合、クエリがリモートテーブルに結合すると、バインドのピークが発生しないことです。
これは、問題の再現に役立つテストケースです。最初に2つのソーステーブルを設定します。1つ目は、30年前の月の最初の日付のリストです。
create table mike_temp_etl_control
as
select
add_months(trunc(sysdate, 'MM'), 1-row_count) as reporting_date
from (
select level as row_count
from dual
connect by level < 360
);
次に、以下から供給されたいくつかのデータdba_objects
:
create table mike_temp_dba_objects as
select owner, object_name, subobject_name, object_id, created
from dba_objects
union all
select owner, object_name, subobject_name, object_id, created
from dba_objects;
次に、空のテーブルを作成して、データを実行します。
create table mike_temp_1
as
select
a.OWNER,
a.OBJECT_NAME,
a.SUBOBJECT_NAME,
a.OBJECT_ID,
a.CREATED,
b.REPORTING_DATE
from
mike_temp_dba_objects a
join mike_temp_etl_control b on (
b.reporting_date between add_months(a.created, -24) and a.created)
where 1=2;
次に、コードを実行します。クエリを遅くするために、より大きなバージョンのmike_temp_dba_objectsを作成する必要がある場合があります(または他の方法を使用して実行プランを取得します)。クエリの実行中に、別のセッションから実行することにより、セッションから実行プランを取得しますselect *
from table(dbms_xplan.display_cursor(sql_id => 'xxxxxxxxxxx'))
。
declare
pv_report_start_date date := date '2002-01-01';
v_report_end_date date := date '2012-07-01';
begin
INSERT /*+ APPEND */
INTO mike_temp_5
select
a.OWNER,
a.OBJECT_NAME,
a.SUBOBJECT_NAME,
a.OBJECT_ID,
a.CREATED,
b.REPORTING_DATE
from
mike_temp_dba_objects a
join mike_temp_etl_control b on (
b.reporting_date between add_months(a.created, -24) and a.created)
cross join dual@emirrl -- This line causes problems...
where
b.reporting_date between add_months(pv_report_start_date, -12) and v_report_end_date;
rollback;
end;
クエリにリモートテーブルがあるため、mike_temp_etl_controlテーブルのカーディナリティの見積もりは完全に間違っており、バインドのピークは発生していないようです。
上記のクエリの実行プランを以下に示します。
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | | 373 (100)|
| 1 | LOAD AS SELECT | | | | |
|* 2 | FILTER | | | | |
| 3 | MERGE JOIN | | 5 | 655 | 373 (21)|
| 4 | SORT JOIN | | 1096 | 130K| 370 (20)|
| 5 | MERGE JOIN CARTESIAN| | 1096 | 130K| 369 (20)|
| 6 | REMOTE | DUAL | 1 | | 2 (0)|
| 7 | BUFFER SORT | | 1096 | 130K| 367 (20)|
|* 8 | TABLE ACCESS FULL | MIKE_TEMP_DBA_OBJECTS | 1096 | 130K| 367 (20)|
|* 9 | FILTER | | | | |
|* 10 | SORT JOIN | | 2 | 18 | 3 (34)|
|* 11 | TABLE ACCESS FULL | MIKE_TEMP_ETL_CONTROL | 2 | 18 | 2 (0)|
---------------------------------------------------------------------------------------
次に、リモートをローカルバージョンに置き換えるdual
と、正しいカーディナリティ(2ではなく139)が得られます。
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | | 10682 (100)|
| 1 | LOAD AS SELECT | | | | |
|* 2 | FILTER | | | | |
| 3 | MERGE JOIN | | 152K| 19M| 10682 (3)|
| 4 | SORT JOIN | | 438K| 51M| 10632 (2)|
| 5 | NESTED LOOPS | | 438K| 51M| 369 (20)|
| 6 | FAST DUAL | | 1 | | 2 (0)|
|* 7 | TABLE ACCESS FULL| MIKE_TEMP_DBA_OBJECTS | 438K| 51M| 367 (20)|
|* 8 | FILTER | | | | |
|* 9 | SORT JOIN | | 139 | 1251 | 3 (34)|
|* 10 | TABLE ACCESS FULL| MIKE_TEMP_ETL_CONTROL | 139 | 1251 | 2 (0)|
-------------------------------------------------------------------------------------
それで、問題は、どのようにして正しいカーディナリティを推定することができるかということだと思います。これはOracleのバグですか、それとも予想される動作ですか?