前回 (CBOにおける索引の使用判断)の続き。
コスト計算方法
表全走査 (Full Table Scan):
[ブロック数] / [DB_FILE_MULTIBLOCK_READ_COUNT]
索引一意走査 (Index Unique Scan):
([索引階層数] + 1{ROWIDによる表走査}) * [OPTIMIZER_INDEX_COST_ADJ] / 100
索引範囲走査 (Index Range Scan):
([索引階層数] - 1{リーフ分を控除} + [リーフブロック数] * [Filtering Factor] + [Clustering Factor] * [Filtering Factor]) * [OPTIMIZER_INDEX_COST_ADJ] / 100
表/索引情報
表T1の情報:
・行数: 1,000,000
・ブロック数: 1,500
・DB_FILE_MULTIBLOCK_READ_COUNT: 8
項目COL1の情報:
・一意な値: 1,000,000件
・最小値: 1
・最大値: 1,000,000
索引の情報:
・索引階層数: 3
・リーフブロック数: 2,000
・Clustering Factor: 1,500
例1: 値指定問い合わせ
SELECT COL1
FROM T1
WHERE COL1 = 990000;
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * (1 / 1,000,000) + 1,500 * (1 / 1,000,000)) * 100/100
= 2 + 0.002 + 0.0015 = 2.0035
結果、索引走査が行われる。
例2: 下限指定問い合わせ
SELECT COL1
FROM T1
WHERE COL1 > 990000;
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * ((1000000 - 990000) / (1000000 - 1)) + 1,500 * ((1000000 - 990000) / (1000000 - 1))) * 100/100
= 2 + 20+ 15 = 37
結果、索引走査が行われる。
例3: 上限指定問い合わせ
SELECT COL1
FROM T1
WHERE COL1 < 990000;
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * ((990000 - 1) / (1000000 - 1)) + 1,500 * ((990000 - 1) / (1000000 - 1))) * 100/100
= 2 + 1980+ 1485 = 3467
結果、全表走査が行われる。
例4: 範囲指定問い合わせ
SELECT COL1
FROM T1
WHERE COL1 BETWEEN 990000 AND 991000;
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * ((991000 - 990000) / (1000000 - 1)) + 1,500 * ((991000 - 990000) / (1000000 - 1))) * 100/100
= 2 + 2 + 1.5 = 5.5
結果、索引走査が行われる。
表/索引情報 その2
表T2の情報:
・行数: 1,000,000
・ブロック数: 1,500
・DB_FILE_MULTIBLOCK_READ_COUNT: 8
項目COL2の情報:
・一意な値: 2件
・最小値: 1
・最大値: 2
・「1」の件数: 1,000
・「2」の件数: 999,000
・ヒストグラム作成済み (サイズ: 2)
索引の情報:
・索引階層数: 3
・リーフブロック数: 2,000
・Clustering Factor: 1,500
このテーブルは索引付けされた項目のカーディナリティ(濃度)が非常に低い。オンライン系のアプリケーションでは、ステータス管理項目にこのような例が見られる。
COL2にはヒストグラムを作成してある。OracleはCOL2のそれぞれの値の件数を情報として持つことになる。これは、Filtering Factorの値が、COL2の値によって変化することを意味する。
例5: 値指定問い合わせ COL2 = 1の場合
SELECT COL2
FROM T2
WHERE COL2 = 1
COL2の値が1の件数は1,000件である。Filtering Factorは、1000 / (1000000 - 1)となる。
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * (1000 / (1000000 - 1)) + 1,500 * (1000 / (1000000 - 1))) * 100/100
= 2 + 2 + 1.5 = 5.5
結果、索引走査が行われる。
例6: 値指定問い合わせ COL2 = 2の場合
SELECT COL2
FROM T2
WHERE COL2 = 2
COL2の値が2の件数は999,000件である。Filtering Factorは、999000 / (1000000 - 1)となる。
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * (999000 / (1000000 - 1)) + 1,500 * (999000 / (1000000 - 1))) * 100/100
= 2 + 1998 + 1498 = 3498
結果、全表走査が行われる。
例7: バインド変数による値指定問い合わせ
SELECT COL2
FROM T2
WHERE COL2 = :X
バインド変数による問い合わせの場合、Oracleは実行計画を立てる際、:Xの値が未知数のため、ヒストグラムによる件数の推理は行えない。この場合のFiltering Factorは下記の様になる。
Filtering Factor = (行数 / 一意な値の数) / 行数 = 1 / 一意な値の数
よって、T2のCOL2については、
Filtering Factor (COL2) = (1000000 / 2) / 1000000 = 1 / 2 = 0.5
となる。
全表走査のコスト:
1,500 / 8 = 187.5
索引走査のコスト:
(3 - 1 + 2,000 * 0.5 + 1,500 * 0.5) * 100/100
= 2 + 1000 + 750 = 1752
結果、全表走査が行われる。
カーディナリティが非常に低い項目に対するバインド変数を使った問い合わせは、可能な限り避けるべきである。バインド変数を使う必要がある場合は、ヒントの使用やOUTLINEの設定を考えるべきである。