述語の値を変更するだけで、同じクエリに対して2つの異なる実行プランを取得しています。最初の1つはネストされたループを使用し、もう1つははるかに長いハッシュ結合を使用します。
explain plan for SELECT tbl0.ID
FROM mcoberturadetalleprimac tbl0
INNER JOIN mcoberturaseccionc tbl1 ON tbl0.mcoberturaseccionc_id = tbl1.ID
INNER JOIN mseccionincisoc tbl2 ON tbl1.mseccionincisoc_id = tbl2.ID
INNER JOIN mincisoc tbl3 on tbl3.id = tbl2.mincisoc_id
WHERE tbl3.mcotizacionc_id = 10371;
Plan hash value: 3143545222
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 47 | 1974 | 82 (2)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | MCOBERTURADETALLEPRIMAC | 1 | 11 | 1 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 47 | 1974 | 82 (2)| 00:00:01 |
| 3 | NESTED LOOPS | | 49 | 1519 | 15 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 5 | 100 | 9 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| MINCISOC | 5 | 50 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IDX_MINCISOC_01 | 5 | | 1 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| MSECCIONINCISOC | 1 | 10 | 1 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | IDX_MSECCIONINCISOC_01 | 1 | | 1 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID | MCOBERTURASECCIONC | 10 | 110 | 1 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | IDX_MCOBERTURASECCIONC_01 | 11 | | 1 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | IDX_MCOBERTURADETALLEPRIMAC_01 | 1 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("TBL3"."MCOTIZACIONC_ID"=10371)
8 - access("TBL3"."ID"="TBL2"."MINCISOC_ID")
10 - access("TBL1"."MSECCIONINCISOC_ID"="TBL2"."ID")
11 - access("TBL0"."MCOBERTURASECCIONC_ID"="TBL1"."ID")
tbl3.mcotizacionc_id
を10371
から368836
に変更するだけで、非常に遅い別のプラン(ハッシュ結合プラン)を取得します。 9秒:
explain plan for SELECT tbl0.ID
FROM mcoberturadetalleprimac tbl0
INNER JOIN mcoberturaseccionc tbl1 ON tbl0.mcoberturaseccionc_id = tbl1.ID
INNER JOIN mseccionincisoc tbl2 ON tbl1.mseccionincisoc_id = tbl2.ID
INNER JOIN mincisoc tbl3 on tbl3.id = tbl2.mincisoc_id
WHERE tbl3.mcotizacionc_id = 368836;
PLAN_TABLE_OUTPUT
Plan hash value: 604514386
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 45922 | 1883K| | 9354 (4)| 00:01:53 |
|* 1 | HASH JOIN | | 45922 | 1883K| 2008K| 9354 (4)| 00:01:53 |
|* 2 | HASH JOIN | | 47806 | 1447K| | 2879 (5)| 00:00:35 |
|* 3 | HASH JOIN | | 4712 | 94240 | | 329 (4)| 00:00:04 |
| 4 | TABLE ACCESS BY INDEX ROWID| MINCISOC | 4712 | 47120 | | 69 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_MINCISOC_01 | 4712 | | | 9 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | MSECCIONINCISOC | 399K| 3905K| | 255 (3)| 00:00:04 |
| 7 | TABLE ACCESS FULL | MCOBERTURASECCIONC | 4057K| 42M| | 2510 (4)| 00:00:31 |
| 8 | TABLE ACCESS FULL | MCOBERTURADETALLEPRIMAC | 3897K| 40M| | 2058 (4)| 00:00:25 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TBL0"."MCOBERTURASECCIONC_ID"="TBL1"."ID")
2 - access("TBL1"."MSECCIONINCISOC_ID"="TBL2"."ID")
3 - access("TBL3"."ID"="TBL2"."MINCISOC_ID")
5 - access("TBL3"."MCOTIZACIONC_ID"=368836)
クエリヒントを使用することで、そのクエリにネストされたループを使用させることができます。これにより、まったく同じ最初の計画(ハッシュ3143545222)が得られ、実行に7 msだけかかります。
SELECT /*+ USE_NL(tbl2 tbl3 tbl1 tbl0) */ tbl0.ID
FROM mcoberturadetalleprimac tbl0
INNER JOIN mcoberturaseccionc tbl1 ON tbl0.mcoberturaseccionc_id = tbl1.ID
INNER JOIN mseccionincisoc tbl2 ON tbl1.mseccionincisoc_id = tbl2.ID
INNER JOIN mincisoc tbl3 on tbl3.id = tbl2.mincisoc_id
WHERE tbl3.mcotizacionc_id = 368836;
私はクエリヒントを使用するべきではなく、クエリオプティマイザが使用するべきだと思います。私はすでに統計を実行しています。これの理由は何でしょうか?
Oracleバージョン:
Oracle Database 10g Release 10.2.0.4.0 - 64bit Production
更新:
私は次のヒストグラムを持っています、それはコメントに従って関連しているかもしれません:
TABLE_NAME COLUMN_NAME NUM_DISTINCT LOW_VALUE HIGH_VALUE NUM_BUCKETS HISTOGRAM
MINCISOC MCOTIZACIONC_ID 80667 C25829 C3255F04 254 HEIGHT BALANCED
NUM_DISTINCT
には80667と記載されていますが、実際にはその列でカウントを実行すると257369:
SELECT count(distinct mcotizacionc_id)
FROM mincisoc
問題はヒストグラムでした。統計を実行してヒストグラムの作成を無効にし、実行計画ではネストされたループを使用していました。
BEGIN
DBMS_STATS.GATHER_table_STATS (OWNNAME => 'MIDAS', TABNAME => 'MINCISOC',
METHOD_OPT => 'FOR ALL COLUMNS SIZE 1');
END;
ハッシュ結合を使用しているため、FOR ALL COLUMNS SIZE AUTO
でもう一度実行すると、同じ問題が発生します。 Philの提案に感謝します。