web-dev-qa-db-ja.com

Oracleが個別のクエリにインデックスを使用しないのはなぜですか?

これはシナリオです

SQL> exec dbms_stats.gather_table_stats(user,'TM', cascade=>true)

PL/SQL procedure successfully completed.

SQL> SELECT SEGMENT_NAME , SEGMENT_TYPE , BYTES / 1024 / 1024 MB , BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME IN ('TM', 'TM_LD_IX');

SEGMENT_NAME            SEGMENT_TYPE        MB        BLOCKS
------------------------------------------ ---------- ----------
TM                      TABLE                 296      37888
TM_LD_IX                INDEX                  46       5888

SQL> select index_name , column_name from user_ind_columns where index_name = 'TM_LD_IX';

INDEX_NAME   COLUMN_NAME
------------ ------------------------------
TM_LD_IX     LD

SQL> explain plan for select distinct LD from TM;

Explained.

SQL> @ex

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------

Plan hash value: 4241255022

--------------------------------------------------------------------------------------
| Id  | Operation          | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                 |   693 |  4158 |  7920   (8)| 00:01:36 |
|   1 |  HASH UNIQUE       |                 |   693 |  4158 |  7920   (8)| 00:01:36 |
|   2 |   TABLE ACCESS FULL| TM              |  2549K|    14M|  7486   (3)| 00:01:30 |
--------------------------------------------------------------------------------------

9 rows selected.

SQL> explain plan for select /*+ index(x , TM_LD_IX) */ distinct LD from TM x;

Explained.

SQL> @ex

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------

Plan hash value: 4241255022

--------------------------------------------------------------------------------------
| Id  | Operation          | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                 |   693 |  4158 |  7920   (8)| 00:01:36 |
|   1 |  HASH UNIQUE       |                 |   693 |  4158 |  7920   (8)| 00:01:36 |
|   2 |   TABLE ACCESS FULL| TM              |  2549K|    14M|  7486   (3)| 00:01:30 |
--------------------------------------------------------------------------------------

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
PL/SQL Release 10.2.0.3.0 - Production
CORE    10.2.0.3.0      Production
TNS for 32-bit Windows: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production

ご覧のとおり、OracleはLDのインデックスを使用しておらず、代わりに全表スキャンを選択しています。私は彼に履歴付きのインデックスを使用させることさえできません。

上記の簡単なクエリでは、TM_LD_IX。ぼくの db_file_multiblock_read_countは32に設定されているため、約5888/32 = 184のコストが予想されます(インデックスを使用すると、一意のハッシュのコストも節約できます)。

だから、私はここで何が欠けていますか?

5
haki

この動作の理由は、LDがNULLである行がインデックスに見つからないためです。したがって、Oracleはテーブル全体をスキャンする必要があります。 LDをNOT NULL列としてテーブルが作成された場合、オプティマイザはこの情報を使用して、INDEX FAST FULL SCANを実行します。列にLD NOT NULLが定義されていない「CHECK(LD is not null)」制約をテーブルに追加すると、オプティマイザは制約によって提供された情報を使用せず、テーブル全体を再度スキャンします。 、あなたが彼にヒントを与えたとしても。 Jonathan Lewis がこの動作について書きました。

次のスクリプトは、Oracle 11.2.0.3.0のこの動作を示しています。

* create_table.sql *はデータをテーブルに挿入し、インデックスと統計を作成します

 set autotrace off 
 drop table objects 
/
 create table objects(
 object_id number、
 owner varchar2(30)、 
 object_name varchar2(128)、
 object_type varchar2(19)
)
/
 
 insert into objects(
 object_id、
 owner、
 object_name、
 object_type 
)
 select 
 object_id、
 owner、 
 object_name、
 object_type 
 from dba_objects 
/
 
 
 create index idx_object_id on objects(object_id); 
 
 exec dbms_stats.gather_table_stats(user、 'objects'、cascade => true)
 

次のスクリプトを実行します。

 spool output 
 set feedback off 
 set linesize 300 
 set trimout on 
 set trimspool on 
 
 @ create_table 
 set autotrace traceonly Explain 
 
 Prompt 
 prompt 1.制約のないクエリの計画:
 select different object_id 
 fromオブジェクト; 
 rem ------------------------------------------ --------- 
 
 @ create_table 
変更テーブルオブジェクトは制約を追加しますnn_object_id check(object_id is not null)validate; 
 set autotrace traceonly 
 
プロンプト
プロンプト2.チェック制約付きのクエリを計画します
 select object_id 
 from objects; 
 rem --- ------------------------------------------------ 
 
 @ create_table 
 alter table objects modify object_id not null; 
 set autotrace traceonly describe 
 
 Prompt 
 prompt 3 .NOT NULL列を使用したクエリの計画
オブジェクトから個別のobject_id 
を選択します; 
 rem --------- ------------------------------------------ 
 
 @ create_table 
オブジェクト上にビットマップインデックスを作成しますbidx_object_type(object_type)
/
 autotraceトレースのみ説明
 
プロンプト
プロンプト4.ビットマップインデックスを使用したクエリの計画
オブジェクトから個別のobject_type 
を選択します; 
 rem -------------------- ------------------------------- 
 
 
 spool off 

これにより、次の出力が得られます

 1。制約のないクエリの計画:
 
実行計画
 -------------------------- -------------------------------- 
プランハッシュ値:4077265613 
 
 ------------------------------------------------ ------------------------------ 
 | Id |オペレーション|名前|行|バイト|コスト(%CPU)|時間| 
 ------------------------------------------- ----------------------------------- 
 | 0 |ステートメントを選択| | 59063 | 288K | 139(3)| 00:00:02 | 
 | 1 | HASH UNIQUE | | 59063 | 288K | 139(3)| 00:00:02 | 
 | 2 |テーブルアクセスフル| OBJECTS | 59063 | 288K | 136(0)| 00:00:02 | 
 --------------------------------------- --------------------------------------- 
 
 
 2。チェック制約付きのクエリの計画
 
実行計画
 --------------------------- ------------------------------- 
プランハッシュ値:4077265613 
 
 ------------------------------------------------- ----------------------------- 
 | Id |オペレーション|名前|行|バイト|コスト(%CPU)|時間| 
 ------------------------------------------- ----------------------------------- 
 | 0 |ステートメントを選択| | 59063 | 288K | 139(3)| 00:00:02 | 
 | 1 | HASH UNIQUE | | 59063 | 288K | 139(3)| 00:00:02 | 
 | 2 |テーブルアクセスフル| OBJECTS | 59063 | 288K | 136(0)| 00:00:02 | 
 --------------------------------------- --------------------------------------- 
 
 
 3。NOT NULL列のあるクエリのプラン
 
実行プラン
 -------------------- -------------------------------------- 
計画ハッシュ値:4172758181 
 
 ------------------------------------------ --------------------------------------------- 
 | Id |オペレーション|名前|行|バイト|コスト(%CPU)|時間| 
 ------------------------------------------- -------------------------------------------- 
 | 0 |ステートメントを選択| | 59063 | 288K | 40(8)| 00:00:01 | 
 | 1 | HASH UNIQUE | | 59063 | 288K | 40(8)| 00:00:01 | 
 | 2 |インデックス高速フルスキャン| IDX_OBJECT_ID | 59063 | 288K | 37(0)| 00:00:01 | 
 --------------------------------------- ------------------------------------------------ 
 
 
 4。ビットマップインデックスを使用したクエリのプラン
 
実行プラン
 ------------ ---------------------------------------------- 
計画ハッシュ値:2970019208 
 
 ---------------------------------- -------------------------------------------------- ------------- 
 | Id |オペレーション|名前|行|バイト|コスト(%CPU)|時間| 
 ------------------------------------------- -------------------------------------------------- ---- 
 | 0 |ステートメントを選択| | 43 | 387 | 6(34)| 00:00:01 | 
 | 1 | HASH UNIQUE | | 43 | 387 | 6(34)| 00:00:01 | 
 | 2 |ビットマップインデックス高速フルスキャン| BIDX_OBJECT_TYPE | 59063 | 519K | 4(0)| 00:00:01 | 
 --------------------------------------- -------------------------------------------------- -------- 
 

まとめ

列に通常のB *ツリーインデックスがある場合、列でNULL値が可能である場合、オプティマイザはインデックスの情報のみに依存して「select distinc」を実行できず、TABLE ACCESS FULLを実行します。

列に通常のB *ツリーインデックスとNOT-NULLチェック制約がある場合、オプティマイザはインデックスの情報に依存せず、TABLE ACCESS FULLを作成します。

通常のB *ツリーインデックスがあり、列がNOT NULLと定義されている場合、オプティマイザはインデックスの情報に依存して、INDEX FAS FULL SCANを実行します。

列にビットマップインデックスがある場合、オプティマイザーはすべての情報がインデックスに含まれていることを認識し、BITMAP INDEX FAST FULL SCANを実行します。

10
miracle173