Oracle のオプティマイザは、複数列の WHERE 条件を評価するとき各列が互いに独立していると仮定してカーディナリティを計算します。しかし実際のデータには列間に相関(例: CITY = ‘Tokyo’ のとき PREFECTURE はほぼ ‘Tokyo’)があることが多く、独立性の仮定により見積もりが大きく外れることがあります。
拡張統計(Extended Statistics)を使うと、複数列の組み合わせや関数式に対して統計を収集でき、オプティマイザの見積もり精度を改善できます。
この記事でわかること
- 列グループ統計(Column Group Statistics)で複数列の相関をオプティマイザに伝える方法
- 式統計(Expression Statistics)で UPPER(col) などの関数式に統計を収集する方法
- DBMS_STATS.CREATE_EXTENDED_STATS で拡張統計を手動作成する
- DBMS_STATS.SEED_COL_USAGE でワークロードから自動的に有用な拡張統計を発見する
- DBA_STAT_EXTENSIONS で拡張統計の一覧を確認する
列グループ統計(Column Group Statistics)
列グループ統計は、複数の列をグループとしてまとめて1つの統計として扱う仕組みです。複数列が相関関係にある場合、単独列の統計の積算より正確なカーディナリティを推定できます。
列グループ統計を手動で作成する
-- DBMS_STATS.CREATE_EXTENDED_STATS で列グループ統計を作成する
-- 戻り値: 作成された拡張統計の内部名(SYS_STU... 形式の仮想列名)
DECLARE
v_ext_name VARCHAR2(30);
BEGIN
-- CITY と PREFECTURE の組み合わせに列グループ統計を作成する
v_ext_name := DBMS_STATS.CREATE_EXTENDED_STATS(
ownname => 'HR',
tabname => 'CUSTOMERS',
extension => '(CITY, PREFECTURE)' -- 列名をカンマ区切りで列挙
);
DBMS_OUTPUT.PUT_LINE('作成された拡張統計名: ' || v_ext_name);
-- → SYS_STU#S#WF25Z#QAHIHE#MOFFMM のような内部名が返る
END;
/
-- 統計を収集する(GATHER_TABLE_STATS で拡張統計も一緒に収集される)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'HR',
tabname => 'CUSTOMERS',
cascade => TRUE -- インデックス統計も同時収集
);
END;
/
拡張統計が作成されたか確認する(DBA_STAT_EXTENSIONS)
-- 作成された拡張統計の一覧を確認する
SELECT
owner,
table_name,
extension_name, -- 内部名(SYS_STU... 形式)
extension, -- 列グループの定義(例: (CITY, PREFECTURE))
creator, -- MANUAL(手動)/ AUTO-FILTER(自動)
droppable -- YES = DROP で削除可能
FROM DBA_STAT_EXTENSIONS
WHERE owner = 'HR'
ORDER BY table_name, extension_name;
-- 拡張統計の実際の統計値は DBA_TAB_COL_STATISTICS で確認する
-- extension_name が仮想列名として登録される
SELECT column_name, num_distinct, num_nulls, last_analyzed
FROM DBA_TAB_COL_STATISTICS
WHERE owner = 'HR'
AND table_name = 'CUSTOMERS'
AND column_name LIKE 'SYS_STU%' -- 拡張統計の仮想列
ORDER BY column_name;
列グループ統計が効果的な場面
列グループ統計の効果を実行計画で確認する
-- 相関のある複数列を条件にするクエリ(列グループ統計なし → 見積もり誤差が大きい) EXPLAIN PLAN FOR SELECT * FROM customers WHERE city = 'Tokyo' AND prefecture = 'Tokyo'; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'TYPICAL')); -- Rows 列の E-Rows(見積もり)と実際の行数を比べる -- 列グループ統計あり → より正確な E-Rows になる -- Note 欄に "column statistics used" は表示されないが、Rows 見積もりが改善される
式統計(Expression Statistics)
式統計は、関数や式に対して統計を収集する仕組みです。WHERE UPPER(last_name) = 'SMITH' のように関数を使ったフィルタ条件では、通常の列統計では正確な見積もりができません。式統計を使うと、UPPER(last_name)という式を1つの仮想列として扱い統計を収集できます。
関数式に対する式統計を作成する
-- UPPER(last_name) に対する式統計を作成する
DECLARE
v_ext_name VARCHAR2(30);
BEGIN
v_ext_name := DBMS_STATS.CREATE_EXTENDED_STATS(
ownname => 'HR',
tabname => 'EMPLOYEES',
extension => '(UPPER(LAST_NAME))' -- 式をそのまま記述する
);
DBMS_OUTPUT.PUT_LINE('式統計名: ' || v_ext_name);
END;
/
-- 統計を収集する
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'HR', tabname => 'EMPLOYEES');
END;
/
-- UPPER(last_name) を条件にしたクエリで式統計が使われる
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE UPPER(last_name) = 'SMITH';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'TYPICAL'));
-- 式統計があることで E-Rows の精度が向上する
SEED_COL_USAGE でワークロードから有用な拡張統計を発見する
DBMS_STATS.SEED_COL_USAGE は、指定した時間内に実行された SQL のワークロードを分析し、拡張統計が有効な列グループを自動的に発見します。
SEED_COL_USAGE でワークロード分析を実行する
-- ステップ①: ワークロードのモニタリングを開始する
-- 指定時間内(ここでは 3600 秒 = 1時間)に実行された SQL を収集する
BEGIN
DBMS_STATS.SEED_COL_USAGE(
sqlset_name => NULL, -- NULL = 現在実行中の SQL を直接収集
owner_name => NULL,
time_limit => 3600 -- 収集期間(秒)
);
END;
/
-- ステップ②: 収集期間中に実際のワークロードを実行する
-- (アプリケーションを通常通り稼働させる)
-- ステップ③: 有用な拡張統計の候補を確認する
SELECT table_name, extension
FROM DBA_COL_USAGE_STATISTICS
WHERE owner = 'HR'
ORDER BY table_name, extension;
-- ステップ④: 推奨される拡張統計をまとめて作成するスクリプトを取得する
SELECT DBMS_STATS.REPORT_COL_USAGE(
ownname => 'HR',
tabname => 'CUSTOMERS'
) FROM DUAL;
-- ステップ⑤: 実際に拡張統計を作成する(REPORT が推奨した内容をもとに手動で実行)
DECLARE
v_ext VARCHAR2(30);
BEGIN
v_ext := DBMS_STATS.CREATE_EXTENDED_STATS('HR', 'CUSTOMERS', '(CITY, PREFECTURE)');
DBMS_STATS.GATHER_TABLE_STATS('HR', 'CUSTOMERS');
DBMS_OUTPUT.PUT_LINE(v_ext);
END;
/
拡張統計を削除する
拡張統計を削除する
-- 特定の拡張統計を削除する(extension_name または extension 定義を指定)
BEGIN
DBMS_STATS.DROP_EXTENDED_STATS(
ownname => 'HR',
tabname => 'CUSTOMERS',
extension => '(CITY, PREFECTURE)' -- 作成時と同じ定義を指定
);
END;
/
-- 削除されたか確認する
SELECT extension_name, extension
FROM DBA_STAT_EXTENSIONS
WHERE owner = 'HR' AND table_name = 'CUSTOMERS';
まとめ
| 種別 | 用途 | 記述例 |
|---|---|---|
| 列グループ統計 | 複数列の相関関係をオプティマイザに伝える | (CITY, PREFECTURE) |
| 式統計 | 関数式(UPPER・TO_CHAR など)への統計収集 | (UPPER(LAST_NAME)) |
- 列グループ統計:複数列の WHERE 条件で見積もり誤差が大きいときに有効。相関のある列のペアに適用する
- 式統計:WHERE UPPER(col) = … のような関数式フィルタで見積もり精度を改善する
- SEED_COL_USAGE:実際のワークロードから自動で有用な列グループ候補を発見できる(作成は手動)
- 拡張統計を作成後は
GATHER_TABLE_STATSで再収集が必要(作成のみでは統計値は空) - DBA_STAT_EXTENSIONS で作成済みの拡張統計を確認し、不要なものは DROP_EXTENDED_STATS で削除する
DBMS_STATS を使った通常の統計収集については DBMS_STATS完全ガイドを参照してください。統計フィードバックや動的サンプリングとの組み合わせについては 適応型クエリ最適化完全ガイドも参照してください。