web-dev-qa-db-ja.com

述語の値を変更した場合、同じクエリの異なる実行プラン

述語の値を変更するだけで、同じクエリに対して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_id10371から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     
1
Alfredo Osorio

問題はヒストグラムでした。統計を実行してヒストグラムの作成を無効にし、実行計画ではネストされたループを使用していました。

BEGIN
  DBMS_STATS.GATHER_table_STATS (OWNNAME => 'MIDAS', TABNAME => 'MINCISOC', 
  METHOD_OPT => 'FOR ALL COLUMNS SIZE 1');
END;

ハッシュ結合を使用しているため、FOR ALL COLUMNS SIZE AUTOでもう一度実行すると、同じ問題が発生します。 Philの提案に感謝します。

2
Alfredo Osorio