◀ 11.

Db2のCREATE INDEX|索引定義・INCLUDEと実行計画の確認

▶

Db2 LUWの索引は対象SQLと現在の定義・制約を調べ、実行計画と実測から検討します。構文例の列に索引を作れば必ず速くなる、という記事ではありません。

Db2の索引定義を調べ計画と実測から承認された変更と比較へ進む図

この記事の要点
  • DB2 でインデックスを作成する SQL 構文
  • 基本: CREATE INDEX 名 ON テーブル名 (列1, 列2);
  • UNIQUE インデックス: CREATE UNIQUE INDEX ... — 重複を許さない
  • 降順: CREATE INDEX ix ON t (col DESC);
  • INCLUDE 句で非キー列を含める(カバリングインデックス)。LUWではUNIQUEが前提で、効果は実行計画/計測で確認
  • 貼りすぎると INSERT/UPDATE が遅くなる。WHERE / ORDER BY / JOIN で頻繁に使う列に絞る

基本構文

次の各CREATEは候補の構文例で、一括実行する推奨索引セットではありません。表/索引のschema・既存の制約/重複・対象の型と列順を先に調べます。UNIQUE索引とUNIQUE制約は関連しても同じカタログ定義ではなく、重複データがあれば通常は一意索引を作成できません。

-- 単一列インデックス
CREATE INDEX idx_users_email ON users (email);

-- 複合インデックス(左から評価される)
CREATE INDEX idx_orders_user_date ON orders (user_id, order_date);

-- UNIQUE(重複不可。実質ユニーク制約)
CREATE UNIQUE INDEX idx_users_email_uniq ON users (email);

-- 降順
CREATE INDEX idx_orders_date_desc ON orders (order_date DESC);

-- 複数の昇降混合
CREATE INDEX idx_logs ON access_logs (user_id ASC, accessed_at DESC);

INCLUDE 句(カバリングインデックス)

LUWのINCLUDEはUNIQUE索引が前提です。追加列は一意性判定のキーに含まれず、キー列と区別します。index-only accessの候補になりますが、必ず表アクセスが省略される/速くなるとは限りません。emailが実際に一意でない表へ、カバリングのためだけにUNIQUEを付けないでください。

次の旧コメントの「索引だけで完結」は保証ではありません。UNIQUEの要件に合う表/列に限り、INCLUDE列とキー列を区別して計画で確認します。?は値のバインドで、作成SQLとSELECTを一緒に本番へ投入しないでください。

-- email で検索したついでに name と age を取りたいケース
CREATE UNIQUE INDEX idx_users_cover
ON users (email)
INCLUDE (name, age);

-- このクエリがインデックスだけで完結する(テーブルアクセスなし)
SELECT name, age FROM users WHERE email = ?;

インデックスの確認・削除

下の旧ブロックはSELECTとDROP/REORGを一括実行しません。表名だけの条件は別schemaの同名表が混ざります。COLNAMESは非推奨のため追加例のINDEXCOLUSEも参照してください。DROPは制約/業務依存へ影響し、REORGも変更/負荷を伴うため、単なる確認の続きとして実行しません。

-- システムカタログから一覧
SELECT TABNAME, INDNAME, COLNAMES
FROM SYSCAT.INDEXES
WHERE TABSCHEMA = 'MYSCHEMA';

-- 特定テーブルのインデックス確認
SELECT INDNAME, UNIQUERULE, COLNAMES
FROM SYSCAT.INDEXES
WHERE TABNAME = 'USERS';

-- インデックス削除
DROP INDEX idx_users_email;

-- インデックスのリビルド(断片化解消)
REORG INDEXES ALL FOR TABLE users;

複合インデックスの「左から評価」ルール

先頭からのキー構成は重要ですが、「先頭条件があれば必ず使う、なければ一切使わない」という規則ではありません。型・選択性・統計・取得列・並び順・費用によるoptimizerの選択を、次の条件ごとに確認します。

CREATE INDEX ix ON orders (user_id, order_date) がある場合:

クエリインデックス使用
WHERE user_id = 1先頭キーの探索候補。採用は計画で確認
WHERE user_id = 1 AND order_date = '2024-01-01'複数キーの絞り込み候補。利用範囲は計画で確認
WHERE user_id = 1 ORDER BY order_dateソート省略候補。方向/条件/計画を確認
WHERE order_date = '2024-01-01'先頭からの範囲探索に制約があるが、全索引scan等を含む採用を一律否定しない
WHERE user_id LIKE '1%'文字列型/照合等の条件による候補。数値IDへ安易にLIKEを使わない
WHERE user_id LIKE '%1'通常の先頭キー範囲探索には向きにくい。索引全体の利用可否とは別

EXPLAIN(実行計画)で使われているか確認

次の旧EXPLAIN例には誤りがあります。EXPLAIN_OPERATORへOBJECT_NAMEを同じ列として問い合わせる例は使わず、対象版のEXPLAIN_OBJECT/STREAM等と区別します。WHEREなしで異なる要求のOPERATOR_IDを混ぜず、追加の要求キー限定例を参照してください。EXPLAIN PLANは計画情報を保存する操作で、既存EXPLAIN表の読取とは別です。db2explnの-q等は対象版の引数を確認し、文字列の'?'を実際のバインド値と扱わないでください。

-- DB2 の EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT * FROM users WHERE email = 'foo@example.com';

-- 結果を見る
SELECT OPERATOR_TYPE, OBJECT_NAME, TOTAL_COST
FROM EXPLAIN_OPERATOR
ORDER BY OPERATOR_ID;

-- ツール経由なら db2expln コマンドが便利
-- db2expln -d MYDB -q "SELECT * FROM users WHERE email = '?'"

インデックスを貼るべき列の選び方

条件インデックス効果
WHERE で頻繁に使う候補。条件の選択性・頻度・取得行数を確認
JOIN の結合キー候補。結合方式・件数・キー順等を確認
ORDER BY / GROUP BY の対象列○ ソート省略可能
カーディナリティ(種類数)が高い列分布や条件値との組合せで判断。単純な比例関係ではない
性別など 2 値しかない列低種類数だけで無効としない。偏り/複合条件/取得割合を確認
UPDATE 頻度が高い列△ INSERT/UPDATE 性能が落ちる
NULL が多い列INCLUDE/EXCLUDE NULL KEYS等の定義・分布・条件で判断し、NOT NULLへの変更を性能目的だけで推奨しない

注意点

  • INSERT / UPDATE / DELETE が遅くなる: 更新/保存容量等の費用も測る。個数の一律目安ではなく実際の業務負荷と制約で判断
  • ディスク容量を消費: キー幅・行数・INCLUDE列・圧縮・作成時一時領域等で見積もる。固定容量を全表の目安にしない
  • 断片化: 統計/状態と効果・ロック/領域/復旧を確認して必要な場合だけ検討。定期実行を無条件に追加しない
  • 使われないインデックス: 監視の範囲/期間・reset/再起動・member/partition・月次処理や制約用途を確認する。短期の未使用だけでDROPしない
  • 主キー / UNIQUE 制約は自動でインデックスが作られる — 二重で貼らない

他 DBMS との CREATE INDEX の違い

機能DB2OracleMySQLPostgreSQL
基本CREATE INDEXCREATE INDEXCREATE INDEXCREATE INDEX
UNIQUEUNIQUEUNIQUEUNIQUEUNIQUE
降順DESCDESCDESC (8.0+)DESC
INCLUDE 句UNIQUEが前提、対象表の制限も確認××○ (11+)
関数インデックス○ (10.5+)○○ (8.0+)○
部分インデックス任意WHEREの部分索引とEXCLUDE NULL KEYS等を同一視しない△ (関数で代用)×○ (WHERE 句)

関数インデックス(DB2 10.5+)

式索引もCREATEできることと採用/高速化は別です。UPPERの照合/文字種・関数制限やYEARでの条件変換を確認し、同じ意味のSQL/結果で比較します。DATE/TIMESTAMPの範囲条件を検討する場合も境界を変えず、索引だけを増やして解決したと断定しません。

-- 大文字小文字を無視した検索を高速化
CREATE INDEX idx_email_upper ON users (UPPER(email));

-- このクエリで idx_email_upper が使われる
SELECT * FROM users WHERE UPPER(email) = UPPER('Foo@Example.com');

-- 日付の年だけを索引化
CREATE INDEX idx_year ON orders (YEAR(order_date));
SELECT * FROM orders WHERE YEAR(order_date) = 2024;

運用

  • RUNSTATS を忘れず: インデックス作成後は RUNSTATS ON TABLE users WITH DISTRIBUTION AND DETAILED INDEXES ALL 等の対象範囲・採取方針/負荷を確認。CREATE INDEX時のCOLLECT STATISTICS等もあり、下記の一律再実行を必須としない
  • 索引作成時の並行アクセス: 旧CREATE INDEX ... ALLOW WRITE ACCESSをLUWの完成構文として使わない。CREATE INDEX本来の表種別/版別の並行読書きと、完了段階等の書込ブロックを公式で確認する。無停止/ロックなしは保証されない
  • REORGの判断: 固定の月次/四半期ではなく、状態・実測効果・業務影響から必要性を判断する

関連

  • パーティション索引: 大規模テーブルではパーティションごとに索引を持つ設計が有効
  • クラスタード索引: CLUSTERの指定と既存行の再配置・再編成を区別する。CREATEだけで全行が並び替わるとはしない
  • テキスト索引: 旧CREATE INDEX ... USING DB2 TEXTはLUWの構文として推奨しない。Db2 Text Searchのdb2ts CREATE INDEX ... FOR TEXT等は別機能で、導入/有効化・索引更新/権限を別途確認

既存索引をschemaと表名で正確に確認する

schema名、表名の順で?へバインドします。INDEXCOLUSEとのJOINも索引schemaと名前の両方を使い、別schemaの同名索引を混ぜません。COLSEQは1始まり、COLORDERのA/Dは昇降順、IはINCLUDE列で一意キーの順序とは別です。式がある場合はEXPRESSION_TEXTも確認します。カタログ参照権限と作成/変更権限は別です。

SELECT i.TABSCHEMA, i.TABNAME, i.INDSCHEMA, i.INDNAME,
       i.UNIQUERULE, c.COLSEQ, c.COLNAME, c.COLORDER,
       c."TEXT" AS EXPRESSION_TEXT
FROM SYSCAT.INDEXES i
LEFT JOIN SYSCAT.INDEXCOLUSE c
  ON c.INDSCHEMA = i.INDSCHEMA
 AND c.INDNAME = i.INDNAME
WHERE i.TABSCHEMA = ?
  AND i.TABNAME = ?
ORDER BY i.INDSCHEMA, i.INDNAME, c.COLSEQ;

特定のExplain要求だけを読む

REVIEWは、既に対象版に合ったExplain表がある確認済みのschemaへ置き換える固定識別子です。入力をSQLへ直接連結しません。?へ順にEXPLAIN_REQUESTER、EXPLAIN_TIME、SOURCE_NAME、SOURCE_SCHEMA、SOURCE_VERSION、EXPLAIN_LEVEL、STMTNO、SECTNOを、その要求の正確な値でバインドします。時刻は適切な型/精度で渡し、最新時刻だけで別要求を混ぜません。

SELECT OPERATOR_ID, OPERATOR_TYPE, TOTAL_COST
FROM REVIEW.EXPLAIN_OPERATOR
WHERE EXPLAIN_REQUESTER = ?
  AND EXPLAIN_TIME = ?
  AND SOURCE_NAME = ?
  AND SOURCE_SCHEMA = ?
  AND SOURCE_VERSION = ?
  AND EXPLAIN_LEVEL = ?
  AND STMTNO = ?
  AND SECTNO = ?
ORDER BY OPERATOR_ID;

TOTAL_COSTはtimerons単位の推定累積コストで、実測ミリ秒ではありません。各operatorの値を単純合算して実行時間ともしません。このSELECTは既に保存された情報の読取であり、EXPLAIN実行・表作成・本番SQLの再実行ではありません。オブジェクト名やstream/結合/述語・実際のsectionの計画は関連表や対象版の整形ツールで照合します。

索引変更の前後で比較する項目

  1. 対象SQL・値/分布・取得行数/順序・業務頻度と、現在のschema/表/索引/制約・統計の状態を記録する。
  2. 同じ結果を返す条件で計画・読取量・時間等を比較する。推定planの変化だけで本番高速化としない。cacheや同時負荷の違いも記録する。
  3. UNIQUEを新しい業務制約として必要とするか、INCLUDEの費用、書込/保存領域/ログへの影響、作成中/完了時のブロックを確認する。
  4. 変更には承認された検証先・時間帯・監視・中断/復旧を用意する。作成/REORG/RUNSTATS/削除を診断の名目で一括実行しない。
  5. 変更後は結果集合/順序・読取と更新・制約の挙動を再確認する。未使用判定は業務周期/全member等の観測範囲を含め、観測なしを削除可としない。

列定義の確認 / 行の複数列UPDATE / DDL抽出と適用 / Db2親ページの子ページから選択してください。

公式:CREATE INDEXの制限/並行アクセス / 索引の列順/INCLUDE / EXPLAIN_OPERATOR / EXPLAIN_STREAM / COLNAMESの非推奨 / Text Search索引。

対象範囲と検証

主にDb2 LUWを対象とし、2026年10月に公式資料と照合しました。元のコード/見出し/リンク/画像と過去版を保持し、旧例の誤記や省略はその直前で区別しています。他DB比較表は概要で、別製品の全機能/構文互換性を保証するものではありません。追加の読取SQLの条件/結合/順序をメモリ内の合成データで検査しましたが、Db2実機の構文/権限/採番/計画/ロック・全構成での変更成功を検証したものではありません。

子ページ

子ページはありません

同階層のページ
  1. DB接続コマンド
  2. データベース一覧の確認
  3. テーブル一覧の確認
  4. テーブル定義の確認
  5. DBの設定確認
  6. テーブルスペースの容量の確認および拡張
  7. データ型
  8. 複数カラムのUPDATE
  9. カラムの追加/削除/変更
  10. 自動番号付け (autoincrement) する方法
  11. インデックスの作成
  12. シーケンスおよびインクリメント(ID列)の違いと確認方法
  13. create table文の生成
  14. 特定スキーマの全テーブルの全カラム情報を取得する方法
  15. 【DB2】エラー一覧
  16. 【DB2】テーブル定義からCREATE TABLE文を生成する方法

最近更新/作成されたページ