27

テーブルの主キーであり、一意のインデックスを持つMYTABLE日付列を持つテーブルがあります。SDATE

このクエリを実行すると:

SELECT MIN(SDATE) FROM MYTABLE

それは即座に答えを出します。同じことが次の場合にも起こります。

SELECT MAX(SDATE) FROM MYTABLE

しかし、両方を一緒にクエリすると、次のようになります。

SELECT MIN(SDATE), MAX(SDATE) FROM MYTABLE

実行にははるかに時間がかかります。計画を分析したところ、min または max のいずれかが照会されると、INDEX FULL SCAN(MIN/MAX) が使用されますが、両方が同時に照会されると、FULL TABLE SCAN が実行されることがわかりました。

なぜ?

テストデータ:

バージョン11g

create table MYTABLE
(
  SDATE  DATE not null,
  CELL   VARCHAR2(10),
  data NUMBER
)
tablespace CHIPS
  pctfree 10
  pctused 40
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    minextents 1
    maxextents unlimited
  );

alter table MYTABLE
  add constraint PK_SDATE primary key (SDATE)
  using index 
  tablespace SYSTEM
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    minextents 1
    maxextents unlimited
  );

負荷テーブル:

declare 
  i integer;
begin
  for i in 0 .. 100000 loop
     insert into MYTABLE(sdate, cell, data)
     values(sysdate - i/24, 'T' || i, i);     
     commit;
  end loop;
end;

統計を収集します。

begin
  dbms_stats.gather_table_stats(tabname => 'MYTABLE', ownname => 'SYS');
end;

プラン1:

ここに画像の説明を入力

プラン2:

ここに画像の説明を入力

4

4 に答える 4

12

インデックス フル スキャンは、インデックスの片側のみにアクセスできます。しているとき

SELECT MIN(SDATE), MAX(SDATE) FROM MYTABLE

あなたは2面を訪問することを要求しています。したがって、列の最小値と最大値の両方が必要な場合、インデックス フル スキャンは実行できません。

ここで見つけることができるより詳細な分析。

于 2012-09-24T13:33:43.960 に答える
6

説明計画は異なります。単一のMINorMAXは を生成しINDEX FULL SCAN (MIN/MAX)ますが、2 つが存在する場合はINDEX FULL SCANorを取得しますFAST FULL INDEX SCAN

違いを理解するには、 a の説明を探す必要がありますFULL INDEX SCAN

フル インデックス スキャンでは、データベースはインデックス全体を順番に読み取ります。

つまり、索引がVARCHAR2フィールドにある場合、Oracleは、たとえば文字「A」で始まるすべてのエントリを含む索引の最初のブロックをフェッチし、最後のエントリまでアルファベット順にすべてのエントリをブロックごとに読み取ります( 「A」から「Z」)。エントリがバイナリ ツリー インデックスでソートされるため、Oracle はこのように処理できます。

説明計画を見るINDEX FULL SCAN (MIN/MAX)と、これは、エントリがソートされているため、MIN. のみに関心がある場合MAX、Oracle は同じアクセス パスを使用できますが、今回は最後のエントリから開始し、「Z」から「A」まで逆方向に読み取ります。

現在のところ、 aFULL INDEX SCANは一方向 (順方向または逆方向) しかなく、両端から同時に開始することはできません。これが、最小値と最大値の両方を要求すると、効率の悪いアクセス方法が得られる理由です。

他の回答で示唆されているように、クエリに重要な効率が必要な場合は、2 つの異なるクエリで最小値と最大値を検索して、独自の最適化を実行できます。

于 2012-09-24T14:16:58.310 に答える
5

1 つのクエリでインデックスの両方のエッジを選択しないようにしてください。次のように別の方法でクエリにアクセスします。

select max_date, min_date
from (select max(sdate) max_date from mytable),
       (select min(sdate) min_date from mytable)

オプティマイザーは、ネストされたループ (この場合は 2 回) で INDEX_FULL_SCAN(MIN/MAX) のインデックスにアクセスします。

ここに画像の説明を入力

于 2012-09-25T14:49:22.117 に答える
2

11.2 では同じ動作は見られないと言わざるを得ません。

次のようにテスト ケースを設定し、Vincent のコメントに応じて 10k 行から 1m 行に更新した場合

set linesize 130
set pagesize 0
create table mytable ( sdate date );

Table created.

insert into mytable
 select sysdate - level
   from dual
connect by level <= 1000000;
commit;

1000000 rows created.


Commit complete.

alter table mytable add constraint pk_mytable primary key ( sdate ) using index;

Table altered.

begin
dbms_stats.gather_table_stats( user, 'MYTABLE' 
                             , estimate_percent => 100
                             , cascade => true
                               );
end;
/

PL/SQL procedure successfully completed.

次に、クエリを実行すると、ほとんど同じように見える説明プランが得られます (さまざまなタイプの INDEX FULL SCAN に注意してください) 。

explain plan for select min(sdate) from mytable;

Explained.

select * from table(dbms_xplan.display);
Plan hash value: 3877058912

-----------------------------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |        |     1 |     8 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE        |        |     1 |     8 |        |      |
|   2 |   INDEX FULL SCAN (MIN/MAX)| PK_MYTABLE |     1 |     8 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

9 rows selected.

explain plan for select min(sdate), max(sdate) from mytable;

Explained.

select * from table(dbms_xplan.display);
Plan hash value: 3812733167

-------------------------------------------------------------------------------
| Id  | Operation    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |     1 |     8 |   252   (0)| 00:00:04 |
|   1 |  SORT AGGREGATE  |        |     1 |     8 |        |          |
|   2 |   INDEX FULL SCAN| PK_MYTABLE |  1000K|  7812K|   252   (0)| 00:00:04 |
-------------------------------------------------------------------------------

9 rows selected.

私の以前の回答から引用するには:

クエリでインデックスを使用しない最も一般的な理由は、次の 2 つです。

  1. 全テーブル スキャンを実行する方が高速です。
  2. 貧弱な統計。

質問に投稿していないものがない限り、私の即時の答えは、このテーブルの統計を収集していない、十分に高い推定パーセントでそれらを収集していない、または を使用analyzeたことです。とは異なり、ベースのオプティマイザー。dbms_stats.gather_table_stats

のドキュメントから引用するにはanalyze

ほとんどの統計を収集するには、DBMS_STATS パッケージを使用します。これにより、統計を並行して収集したり、パーティション化されたオブジェクトのグローバル統計を収集したり、他の方法で統計収集を微調整したりできます。DBMS_STATS パッケージの詳細は、『Oracle Database PL/SQL パッケージおよびタイプのリファレンス』を参照してください。

コストベースのオプティマイザに関連しない統計収集には、(DBMS_STATS ではなく) ANALYZE ステートメントを使用します。

于 2012-09-24T13:26:27.700 に答える