SQL ステートメントをコンパイルするとき、 SQL 最適化プログラムは別の方法で要求を満たす場合の実行コストを見積もります。 この評価に基づいて、 最適化プログラムは最適のアクセス・プランと思われるものを選択します。 アクセス・プラン とは、 SQL ステートメントの解決に必要な操作の順序を指定するものです。 アプリケーション・プログラムがバインドされると、 パッケージ が作成されます。 このパッケージには、 そのアプリケーション・プログラムの静的 SQL ステートメント全部に関するアクセス・プランが入れられます。 動的 SQL のアクセス・プランは、アプリケーション実行時に作成されます。
表内のデータにアクセスする方法は 2 通りあります。 表を直接読み取る (関係走査) か、 または最初に表の索引にアクセスする (索引走査) かのいずれかです。
関係走査 が行われるのは、 データベース・マネージャーが表の各行に順次アクセスするときです。 索引走査がどのように機能するかについては、 索引走査の概念を参照してください。 また、それぞれのタイプの走査がどのような条件で使用されるかについては、 関係走査と索引走査を参照してください。
以下のトピックでは、表のデータにアクセスし、 照会の結果を生成するためのアクセス・プランで使用可能なその他の方法について説明します。
その他の関連トピック:
索引走査 が行われるのは、 データベース・マネージャーが以下のいずれかまたは全部を行うために索引にアクセスするときです。
索引に対する走査は、定義した方向とは逆方向に実行することもできます。 詳細は、SQL 解説書 に載せられている CREATE INDEX ステートメントの ALLOW REVERSE SCANS オプションを参照してください。
ここでは、以下の追加トピックを示します。
データベース・マネージャーは、 索引の保管に B+ ツリー構造を使用します。 B+ ツリーには、1 つまたは複数のレベルがあり、 それについては次の図に示します (ここで、RID は行 ID を意味します)。
![]() |
最上レベルはルート・ノード と呼ばれます。 最下レベルは、リーフ・ノード で構成され、 ここには実際の索引キー値と、表の実際の行に対するポインターが保管されます。 ルート・ノードとリーフ・ノードの間のレベルは、中間ノード と呼ばれます。
特定の索引キー値を検出するとき、索引マネージャーは、 ルート・ノードから開始して、その索引ツリーを検索します。 ルートには、その次のレベルの各ノードごとに 1 つずつキーが含まれています。 それらの各キーの値は、 その次のレベルの対応するノードに存在する最大のキー値です。 たとえば、図 79 に示すように、 索引に 3 つのレベルがある場合に索引キー値を検出するには、 索引マネージャーは、ルート・ノードから、 目的のキー以上のキー値のうちの最初のものを探索します。 このルート・ノード・キーは、多くの場合、 特定の中間ノードを指すものです。 その中間ノードで同じ手順に従い、 どのリーフ・ノードに進むべきかを決めます。 リーフ・ノードで最終索引キーが検出されます。 図 79 の場合、検索するキーは "I" です。 ルート・ノードの中で、"I" 以上の最初のキーは "N" です。 これは、その次レベルの中間ノードを指すものです。 その中間ノードの中で、"I" 以上の最初のキーは "L" です。 これは、"I" の索引キーとそれに対応する行 ID (基本表の中の対応する行の行 ID) の含まれる特定のリーフ・ノードを指すものです。
注: | リーフ・ノード・レベルでは、前のリーフ・ポインターが存在する場合があります。 ツリーをくまなく検索することによって索引内に特定のキー値を見つけると、 索引マネージャーはいずれかの方向にあるリーフ・ノードを走査して特定範囲の値を検索できるため、 前のリーフ・ポインターがあることが非常に役立つことがあります。 いずれかの方向に走査を実行するこの機能は、 ALLOW REVERSE SCANS パラメーターを使用して索引が作成された場合にのみ使用可能です。 |
詳細は、SQL 解説書 に載せられている CREATE INDEX ステートメントのオプションを参照してください。
ある特定の照会に索引を使用できるかどうかを決定するとき、 最適化プログラムは索引の各列を最初の列から順に評価し、 次のものを満たすことができるかどうかを調べます。
述部 は、WHERE 文節内で比較操作を明示する、 または暗黙のうちに示す探索条件の 1 つの要素です。 索引走査の範囲を区切るのに使用できる述部は、 次の条件が真となる索引列の関係する述部です。
たとえば、索引が次のように定義されているとします。
INDEX IX1: NAME ASC, DEPT ASC, MGR DESC, SALARY DESC, YEARS ASC
索引 IX1 の走査範囲を区切るのには、次の述部を使用することができます。
WHERE NAME = :hv1 AND DEPT = :hv2
または
WHERE MGR = :hv1 AND NAME = :hv2 AND DEPT = :hv3
2 番目の例では、WHERE 述部を、 索引内でキー列の現れる順序と同じ順序で指定する必要はありません。 また、例の中ではホスト変数が使用されていますが、 パラメーター・マーカー、式、または定数には同じ効果があります。
CREATE INDEX ステートメント上で ALLOW REVERSE SCANS パラメーターを使用して作成された単一索引は、 前方向にも後方向にも走査することができます。 つまり、それらの索引では、索引の作成時に定義した方向への走査と、 反対方向 (逆方向) への走査がサポートされています。 ステートメントは、以下のようになります。
CREATE INDEX iname ON tname (cname DESC) ALLOW REVERSE SCANS
この場合、索引 (iname) は、 cname の DESCending 値に基づいて形成されます。 逆方向走査を許可することにより、 列に対する索引が降順で走査するように定義されているとしても、 昇順で走査することができます。 索引を実際に両方向で使うことは、ユーザーが制御するのではなく、 アクセス・プランの作成および考慮時に最適化プログラムによって制御されます。
以下の WHERE 文節では、NAME および DEPT の述部だけが索引走査の範囲を区切るのに使用され、 SALARY または YEARS の述部は使用されません。
WHERE NAME = :hv1 AND DEPT = :hv2 AND SALARY = :hv4 AND YEARS = :hv5
これは、これらの列を最初の 2 つの索引キー列から分離するキー列 (MGR) があるので、 並べ替えがオフになるためです。 しかし、いったん NAME = :hv1 および DEPT = :hv2 の述部によって範囲が決まったなら、 残りの述部を残りの索引キー列に対して評価できるようになります。
前述の等号述部以外にも、特定の不等号述部を使用して、 索引走査の範囲を区切ることができます。 次に、2 種類の不等号述部 (狭義不等号と広義不等号) について説明します。
狭義不等号述部: 範囲を 区切る述部として使用できる狭義不等号演算子は、> と < です。
索引走査の範囲を区切る場合、 狭義不等号述部では 1 つの列だけが考慮されます。 以下の例では、NAME 列と DEPT 列に対して述部を使用して範囲を区切ることはできますが、 MGR 列に対しては述部は使用できません。
WHERE NAME = :hv1 AND DEPT > :hv2 AND DEPT < :hv3 AND MGR < :hv4
広義不等号述部: 以下は、 範囲を区切る述部として使用できる広義不等号演算子です。
索引走査の範囲を区切る場合、 広義不等号述部については複数の列が考慮されます。 次の例では、述部をすべて使用して索引走査の範囲を区切ることができます。
WHERE NAME = :hv1 AND DEPT >= :hv2 AND DEPT <= :hv3 AND MGR <= :hv4
この例についてさらに考慮するために、:hv2 = 404、 :hv3 = 406、および :hv4 = 12345 を想定してください。 データベース・マネージャーは部門 404 と 405 のすべての索引を走査しますが、 12345 より多い従業員数 (MGR 列) を持つ管理者を最初に検出した時点で、 部門 406 の走査を停止します。
詳細については、範囲区切り述部と索引検索引き数述部を参照してください。
照会に並び替えが関係している場合、並び替える列が、 最初の索引キー列から始まって連続して索引内に現れる場合は、 データを並び替えるのに索引を使用することができます。 (並び替えまたは分類は、 ORDER BY、 DISTINCT、 GROUP BY、 "= ANY" 副照会、 "> ALL" 副照会、 "< ALL" 副照会、 INTERSECT または EXCEPT、 UNION などの操作の結果得られます。) ただし、索引キー列が "定数値" (つまり評価結果が定数の式) に等しいかどうかを比較する場合は例外です。 この場合、並べ替えに使う列が最初の索引キー列以外のものである可能性があります。 たとえば、
WHERE NAME = 'JONES' AND DEPT = 'D93' ORDER BY MGR
という照会では、NAME と DEPT は必ず同じ値となり、 結果としてその列については分類されることになるため、 行を並び替えるのに索引を使用できます。 つまり、これは先行する WHERE 文節と ORDER BY 文節とが次のようになっているのと同じことになります。
WHERE NAME = 'JONES' AND DEPT = 'D93' ORDER BY NAME, DEPT, MGR
このほかにも固有索引を使用することによって、 順序要件を切り捨てることもできます。 たとえば、次のような索引の定義と、ORDER BY 文節とがあるとします。
UNIQUE INDEX IX0: PROJNO ASC SELECT PROJNO, PROJNAME, DEPTNO FROM PROJECT ORDER BY PROJNO, PROJNAME
IX0 索引により PROJNO が固有になるため、PROJNAME 列での分類は不要です。 この固有性により、各 PROJNO 値には、PROJNAME 値が 1 つしかないことになります。
場合によっては、表にアクセスせずに、 索引からすべての必須データを検索できることがあります。 これは、索引のみの アクセスと呼ばれます。
以下の索引定義を使って、索引のみのアクセスについて説明します。
INDEX IX1: NAME ASC, DEPT ASC, MGR DESC, SALARY DESC, YEARS ASC
基本表を読み取らずに、索引にアクセスするだけで、以下の照会を実行することができます。
SELECT NAME, DEPT, MGR, SALARY FROM EMPLOYEE WHERE NAME = 'SMITH'
その他の場合は、索引内に現れない列が含まれていることもあります。 それらの列のデータを得るには、基本表の行を読み取らなければなりません。 たとえば、上記の IX1 索引で、以下の照会では、 PHONENO および HIREDATE 列データを取得するには、 基本表にアクセスする必要があります。
SELECT NAME, DEPT, MGR, SALARY, PHONENO, HIREDATE FROM EMPLOYEE WHERE NAME = 'SMITH'
組み込み列のある固有索引を作成し、索引だけに基づくアクセス試行数を多くすると、 データ検索のパフォーマンスを改善できます。
以下の索引定義を使って、組み込み列の使用法について説明します。
CREATE UNIQUE INDEX IX1 ON EMPLOYEE (NAME ASC) INCLUDE (DEPT, MGR, SALARY, YEARS)
この場合、NAME 列を強制的に固有なものにし、しかも DEPT、 MGR、 SALARY、 および YEARS 列のデータも保管して保守する固有索引が作成されます。
基本表を読み取らずに、索引にアクセスするだけで、以下の照会を実行することができます。
SELECT NAME, DEPT, MGR, SALARY FROM EMPLOYEE WHERE NAME='SMITH'
前述のすべての例では、 結果を引き出すのに単一索引走査が実行されました。 WHERE 文節の述部を満たすために、 最適化プログラムでは複数索引の走査を選択することがあります。 たとえば、以下の 2 つの索引定義があるとします。
INDEX IX2: DEPT ASC INDEX IX3: JOB ASC, YEARS ASC
この 2 つの索引を使って、以下の述部が解決されることになります。
WHERE DEPT = :hv1 OR (JOB = :hv2 AND YEARS >= :hv3)
この例では、索引 IX2 を走査することによって、 DEPT = :hv1 述部を満たす行 ID (RID) のリストが作成されます。 また、索引 IX3 を走査することによって、 JOB = :hv2 AND YEARS >= :hv3 述部を満たす RID のリストが作成されます。 これらの 2 つの RID リストを組み合わせると、 表にアクセスする前に重複が除かれます。 これは、索引 OR 操作 と呼ばれます。
索引 OR 操作は、 次の例のように IN 式を使用している述部にも使用できます。
WHERE DEPT IN (:hv1, :hv2, :hv3)
索引 OR 操作の目的は、 重複した RID を除去することですが、 索引 AND 結合 の目的は、 共通 RID を検索することです。 索引 AND 結合は、同じ表内の対応する列に複数の索引があり、 さらに複数の "AND" 述部を使用する照会をこの表に対して実行するアプリケーションにおいて行われます。 その種の照会において、 索引付けされた列ごとに複数の索引走査を行うと、 ビットマップを作成するためにハッシュされた値が生成されます。 2 番目のビットマップは 1 番目のビットマップをプローブするのに使用され、 最終的に戻されるデータ・セットを作成するために取り出される修飾行を生成します。
たとえば、以下の 2 つの索引定義があるとします。
INDEX IX4: SALARY ASC INDEX IX5: COMM ASC
この 2 つの索引を使って、以下の述部が解決されることになります。
WHERE SALARY BETWEEN 20000 AND 30000 AND COMM BETWEEN 1000 AND 3000
この例では、索引 IX4 を走査すると、 SALARY BETWEEN 20000 AND 30000 述部を満たすビットマップが生成されます。 IX5 の走査および IX4 のビットマップのプローブを行うと、 両方の述部を満たす修飾 RID のリストが生成されます。 これは、「動的ビットマップ AND 結合」と呼ばれます。 これは、表が十分なカーディナリティーを持っていて、 さらに列の修飾範囲内に十分な値がある (または、 等号述部が使用される場合は多数の重複がある) 場合にのみ行われます。
注: | 単一表にアクセスする場合には、DB2 は、 索引 AND 結合と索引 OR 結合の結合を行いません。 |
アクセス・プランを選択するとき、 最適化プログラムはディスクからページをバッファー・プールに取り出すときの入出力コストを考慮します。 その計算の中で、最適化プログラムは照会に必要な入出力の数を見積もります。 この見積もりには、バッファー・プール使用率の予測も含まれています。 すでにバッファー・プールの中にあるページに含まれている行を読み取るには、 追加の入出力が必要ないためです。
索引走査の場合、最適化プログラムは、 システム・カタログ表 (SYSCAT.INDEXES) の情報を使用して、 データ・ページをバッファー・プール内に読み込むための入出力コストを見積もります。 SYSCAT.INDEXES 表のうち次の列が使用されます。
または
統計が利用不能な場合は、最適化プログラムは統計の省略時値を使います。 この値は、索引に関するデータのクラスター化率が少ないことを想定しています。 第 24 章, システム・カタログ統計と RUNSTATS ユーティリティーを使用しての統計収集も参照してください。
表の再編成時に行をクラスター化し、 挿入処理の間中この特性を保持するのに使用するクラスター化索引を指定することができます。 (表の再編成については、カタログとユーザー表の再編成を参照してください。) 以降に更新および挿入を行うと、 そのような索引のクラスター化が低下するため (RUNSTATS で収集される統計によって測定できます)、 定期的に表を再編成することが必要な場合があります。 揮発データベースに対する再編成の頻度を下げるには、 表の変更時に PCTFREE パラメーターを使用します。 これで、追加の挿入が既存のデータでクラスター化されます。
索引のデータがどの程度クラスター化されているかによってパフォーマンスに重大な影響を与える可能性があるため、 表の索引の 1 つを 100% クラスター化に近く維持してください。
一般的に、キーがクラスター索引のキーのスーパーセットである場合、 または 2 つの索引のキー列間に事実上の相関がある場合には、 1 つの索引だけを 100% クラスター化することはできません。
クラスター索引を使用するパフォーマンス上の理由について詳しくは、 索引の管理に関するパフォーマンスについてのヒントを参照してください。 クラスター化索引の作成方法の詳細については、 SQL 解説書 で CREATE INDEX を参照してください。
事前取り出しを使用したクラスター化ページの読み取り: 最適化プログラムが索引を使用して行にアクセスする場合、 すべての RID (行識別子) が索引から取得されるまで、 データ・ページの読み取りを据え置くことがあります。 たとえば、前に定義された索引 IX1、
INDEX IX1: NAME ASC, DEPT ASC, MGR DESC, SALARY DESC, YEARS ASC
および、次の探索基準があるとします。
WHERE NAME BETWEEN 'A' and 'I'
最適化プログラムは IX1 上で索引走査を実行することによって、 取り出す行 (およびデータ・ページ) を決めます。 この索引に従ってデータがクラスター化されていなかった場合、 リスト事前取り出しには、 索引操作から獲得された RID のリストを分類するステップが含まれることになります。 詳細については、リスト事前取り出しについてを参照してください。
該当する場合、データベース管理プログラムは索引ページに対する順次アクセスを検出し、 事前取り出し要求を生成します。 これは、非選択索引走査および索引の重要な部分にアクセスする選択索引走査の経過時間を大幅に縮小するものです。
最適化プログラムは、DENSITY および SEQUENTIAL_PAGES などの索引統計、 索引が常駐する表スペースの特性、 および範囲区切り述部の結果を使用して、行われる索引ページ事前取り出しの量を見積もります。 それらの見積もりが、特定の索引を使用するための合計コスト見積もりに入れられます。
詳細は、順次事前取り出しについてを参照してください。
照会に索引を使用できない場合、 または索引走査の方がコストがかかると最適化プログラムが判断した場合、 最適化プログラムは関係走査を選択します。 次のような場合には、索引走査の方がコストが高くなります。
アクセス・プランで関係走査を使うかそれとも索引走査を使うかを、 SQL Explain 機能を使用して判別することもできます。 第 26 章, SQL Explain 機能を参照してください。
ユーザー・アプリケーションは、SQL ステートメントで、 述部を使用して必要な特定の行を修飾することによって、 データベースから一連の行を要求します。 最適化プログラムが SQL ステートメントの評価方法を決定するとき、 各述部は 4 つのカテゴリーのいずれかに分類されます。 カテゴリーは、評価プロセスでその述部が使用される方法、 および使用される時によって判別されます。 それらのカテゴリーをパフォーマンスの点で高いものから順に示すと、 次のようになります。
検索引き数 (SARGable) とは、 search argument (検索引き数) として使用可能なものを表します。
述部の使用法の要約では、 さまざまな述部カテゴリーのパフォーマンスに影響を与える特性を比較しています。
範囲区切り述部は、 索引走査のブラケットに使用するものです。 これらの述部は、索引探索のキー値を開始または停止 (あるいはその両方) します。 索引検索引き数述部は、探索のブラケット化には使用しませんが、 述部に関係する列は索引キーの一部であるため、 索引から評価することはできます。 たとえば、以前に定義した索引 IX1 (索引走査の概念を参照) と、 次の WHERE 文節とがあるとします。
WHERE NAME = :hv1 AND DEPT = :hv2 AND YEARS > :hv5
この例では、最初の 2 つの述部 (NAME = :hv1、DEPT = :hv2) は、 範囲区切り述部であり、YEARS > :hv5 は索引検索引き数述部です。
データベース・マネージャーは、これらの述部を評価するときに、 基本表を読み取るのではなく索引データを使用します。 これらの索引検索引き数 述部によって、 表からの読み取りに必要な一連の行が少なくなり、 アクセスするデータ・ページの数が少なくなります。 これらの種類の述部は、 アクセスする索引ページの数には影響しません。
索引マネージャーによっては評価できないが、 データ・マネージャー・サービスによって評価できる述部は、 データ検索引き数 述部と呼ばれます。 一般的に、これらの述部では、基礎表の個々の行をアクセスする必要があります。 必要なら、データ・マネージャー・サービスは述部を評価するのに必要な列を取り出し、 さらに SELECT リスト内の列を満たすもののうち獲得できなかったものを索引から取り出します。
たとえば、PROJECT 表について、 次の単一の索引が定義されているものとします。
INDEX IX0: PROJNO ASC
この場合、次の照会では、DEPTNO = 'D11' 述部がデータ検索引き数とみなされます。
SELECT PROJNO, PROJNAME, RESPEMP FROM PROJECT WHERE DEPTNO = 'D11' ORDER BY PROJNO
一般にその他の述部は、 基本表の単純アクセスを超えた入出力を必要とするものです。 その他の述部の例としては、相関副照会を使用するもの、 多値副照会 (ANY、ALL、SOME、または IN による副照会) を使用するもの、 または LONG VARCHAR あるいは LOB のデータ (表とは別個のファイル内に保管されるデータ) を読み取るものがあります。 これらの述部は、リレーショナル・データ・サービスによって評価されます。
データ・ページにアクセスする際に、 索引だけに適用される述部を再適用しなければならないことがあります。 たとえば、索引 OR 結合または索引 AND 結合を使用するアクセス・プラン (複数の索引アクセスを参照) は、 データ・ページにアクセスする際に、述部をその他の述部として再適用します。
照会に述部を使うと、
その照会を満たすために読み取られるデータの量を減らすことができます。
述部カテゴリーが異なると照会のパフォーマンスに与える影響も異なりますが、
最適化プログラムはこの影響を考慮します。
以下の表では、さまざまな種類の述部のランクを示し、
それぞれの種類の述部がパフォーマンスに与える影響を記述しています。
特性 | 述部タイプ | |||
---|---|---|---|---|
範囲区切り | 索引検索引き数 | データ検索引き数 | その他 | |
索引入出力の低減 | はい | いいえ | いいえ | いいえ |
データ・ページ入出力の低減 | はい | はい | いいえ | いいえ |
内部的に渡される行数の低減 | はい | はい | はい | いいえ |
修飾行の数の低減 | はい | はい | はい | はい |
結合 とは、 1 つの表の行を、他の 1 つまたは複数の表の行に連結するということです。 たとえば、次の 2 つの表があるとします。
TABLE1 TABLE2 ----------------- ----------------- PROJ PROJ_ID PROJ_ID NAME ------ ------- ------- ------ A 1 1 Sam B 2 3 Joe C 3 4 Mary D 4 1 Sue 2 Mike
表の ID 列が等しい場合に Table1 と Table2 を結合するには、 次に示した SQL ステートメントを使用します。
SELECT PROJ, x.PROJ_ID, NAME FROM TABLE1 x, TABLE2 y WHERE x.PROJ_ID = y.PROJ_ID
さらに、以下の一連の結果行が生成されます。
PROJ PROJ_ID NAME ------ ------- ------ A 1 Sam A 1 Sue B 2 Mike C 3 Joe D 4 Mary
2 つの表を結合すると、1 つの表は外部表として選択され、 もう一方の表は内部表として選択されます。 外部表は最初にアクセスされ、1 回だけ走査されます。 内部表が複数回走査されるかどうかは、 結合の種類および索引がどんなものかによります。 照会によって 2 つ以上の表が結合されるとしても、 それに関係なく最適化プログラムは 1 回に 2 つの表だけを結合します。 必要であれば、一時的な中間結果表を作成します。
最適化プログラムは、 結合述部 (マージ結合を参照) が存在するかどうか、 また表と索引の統計によって判別される必要な各種コストに応じて、 2 つの結合方式 (ネストしたループ結合とマージ結合) のいずれかを選択します。
ネストされたループ結合は、次の 2 つの方法のいずれかで実行されます。
たとえば、表 T1 と T2 の列 A の値が次のようになっている場合、
外部表 T1: 列 A 内部表 T2: 列 A ------------------------ ------------------------ 2 3 3 2 3 2 3 1
ネストしたループを行うステップは以下のとおりです。
この方法は、 次の形式の述部が存在している場合に、指定された述部に使用できます。
expr(outer_table.column) relop inner_table.column
ここで、relop は比較演算子 (たとえば、=、>、>=、<、 または <=) であり、expr は外部表で有効な式です。 以下は、その例です。
OUTER.C1 + OUTER.C2 <= INNER.C1
および
OUTER.C4 < INNER.C3
この方法を使用すると、 外部表の各アクセスごとに内部表でアクセスされる行の数を大幅に減らすことができます (結合述部の選択可能性を含め、 いくつかの要因に依存しています)。
ネストしたループ結合を評価するとき、最適化プログラムは、 結合を実行する前に外部表を分類するかどうかも決定します。 結合列に基づいて外部表を並べ替えるなら、 ページがすでにバッファー・プール内に存在する確率が高くなるため、 内部表でディスクからページにアクセスするための読み取り操作の数が少なくなります。 結合で高度にクラスター化された索引を使用して内部表にアクセスする場合、 外部表が分類されているなら、 アクセスされる索引ページの数を最小限にとどめることができます。
さらに、最適化プログラムは、結合後に分類を行うとコストが高くなると予期した場合に、 結合前に分類を実行するよう選択することがあります。 GROUP BY、DISTINCT、ORDER BY、またはマージ結合をサポートするには、 結合後の分類が必要になります。
マージ結合 (走査マージ結合または分類マージ結合ということもある) には、 table1.column = table2.column. という形式の述部が必要です。 これは、等価結合述部 と呼ばれます。 マージ結合には、索引アクセスによって、 または分類によって、結合する列での入力の並べ替えが必要になります。 マージ結合を使用するためには、 結合列を LONG フィールド列やラージ・オブジェクト (LOB) 列にすることはできません。
結合された表は、同時に走査されます。 マージ結合の外部表は 1 回だけ走査されます。 外部表に繰り返される値がない場合には、内部表も 1 回だけ走査されます。 外部表に繰り返される値がある場合には、 内部表の中の行のグループが再度走査されることがあります。 たとえば、表 T1 と T2 の列 A の値が次のようになっている場合、
外部表 T1: 列 A 内部表 T2: 列 A ------------------------ ------------------------ 2 1 3 2 3 2 3 3
マージ結合を行うためのステップは、次のとおりです。
ハッシュ結合には table1.columnX = table2.columnY の形式の述部が 1つ以上必要で、 これらの列のタイプは同じでなければなりません。 タイプが CHAR の列の場合は、長さが同じでなければなりません。 タイプが DECIMAL の列の場合は、精度と位取りが同じでなければなりません。 列のタイプを LONG フィールド列やラージ・オブジェクト (LOB) 列にすることはできません。
まず 1 つ目の表 (「内部」表) が走査され、 分類ヒープ割り振りによって描画されたメモリー・バッファーに行がコピーされます (分類ヒープ・サイズ (sortheap) データベース構成パラメーターを参照)。 このメモリー・バッファーは、 結合述部の列から計算された「ハッシュ・コード」に基づく区分に分割されています。 1 つ目の表のサイズが使用可能な分類ヒープのスペースより大きい場合は、 選択した区分から一時表にバッファーが書き込まれます。 内部表の処理が終わると、2 つ目の表 (「外部」表) が走査されます。 次に外部表の行と内部表の行が突き合わされます。 そのためには、 まず結合述部の列から生成された「ハッシュ・コード」が比較されます。 次に、外部行の「ハッシュ・コード」と内部行の「ハッシュ・コード」が一致していると、 実際の結合述部の列が比較されます。
一時表に書き込まれていない区分に対応した外部表の行は、 メモリー内の内部表の行と直ちに突き合わされます。 一方、対応する内部表の区分が一時表に書き込まれている場合は、 外部行も一時表に書き込まれます。 最後に、一致している区分の組みが一時表から読み取られ、 それらの行の「ハッシュ・コード」が突き合わされ、結合述部が検査されます。
ハッシュ結合のパフォーマンス上の効果を理解するには、 sortheap データベース構成パラメーターの値、 および sheapthres データベース・マネージャー構成パラメーターの値を変更する必要があります。
意思決定支援の照会の場合、ハッシュ結合アクセス・プランは、 それ以外の場合よりも多くの分類ヒープ・スペースを使用します。 sheapthres が sortheap と比較的近い値に設定されている場合 (つまり、 並行する照会当たり 2 か 3 になる係数より小さい値)、 ハッシュ結合を使用すれば、最適化プログラムを使用するときよりも、 使うメモリーの量は少なくて済みます。 メモリーが限られている状況で実行するときには、 ハッシュ結合は非常に遅くなる可能性があります。 分類またはハッシュ結合では、使用可能メモリーのほとんどを使うため、 複数の分類およびハッシュ結合を処理すると、照会中に問題が発生します。
この解決策は、sheapthres を (sortheap と比較して) 十分な大きさに構成することです。
結合時に、 内部表と外部表はどのように判別されるのでしょうか。 以下は、 最適化プログラムが内部表および外部表を決定する方法についての一般的な指針です。
ハッシュ結合の場合は、 内部表はメモリー・バッファーに保持されます。 メモリー・バッファーが少な過ぎる場合は、ハッシュ結合が分割されます。 最適化プログラムはこれを避けようとするので、 2 つの表の小さい方を内部表として、大きい方を外部表として用います。
表にアクセスする順序は、ネストしたループ結合では特に重要です。 というのも、外部表は 1 回しかアクセスされませんが、 内部表は外部表の各行ごとに 1 回ずつアクセスされるためです。 最適化プログラムは、コスト見積もりに基づいて外部表と内部表を選択します。 これらのコスト見積もりは、以下の要因によって影響を受けます。
内部表に再アクセスしなければならない回数を少なくするため、 多くの場合、小さい方の表が外部表として選択されます。 しかし、事前取り出しを行うと、この逆になることがあります。 事前取り出しを行うと、 大きな表にアクセスするコストがかなり少なくなります。 しかし通常は、事前取り出しが効果的なのは結合の外部表だけです。 したがって、大きな表が最初にアクセスされることがあります。 詳細は、バッファー・プールへのデータの事前取り出しを参照してください。
選択述部を表に適用できる場合、 内部表にアクセスするのは外部表に適用される述部を満たす行についてだけなので、 表は外部表として選択される可能性が高くなります。
外部表の各行ごとに内部表全体を走査しなければならない場合 (つまり、 内部表に対して索引参照を実行できない場合) は、 バッファリングを利用できるようにするため、 2 つの表のうち小さい方が内部表として選択されます。 これは、表サイズやバッファー・プール・サイズによって影響を受けます。 結合の決定は、バッファー・プール・サイズによって影響されるため、 バッファー・プール・サイズを変更した後でアプリケーションをデータベースに再バインドすると、 アプリケーションのアクセス・プランが変わる場合があります。
複数のバッファー・プールの作成、そのバッファー・プールのサイズの変更、 さらにそのバッファー・プールを使用する表スペースの制御を行う能力は、 内部表および外部表内でバッファリングが使用されると、影響を受けます。
どれか 1 つの表上で索引検索を実行できる場合、 その表が内部表としての使用に最も適しています。 その場合、その表は、外部表の結合キー述部をキー値の 1 つとして使用して、 索引キー参照によってアクセスされます。 表に索引がない場合は、 外部表の各行ごとに内部表全体を走査しなければならないため、 その表は内部表としてよい候補とは言えません。
必要な順序を伴う表は、最初にアクセスするようにできます。 たとえば、t1 と t2 の間の結合の出力が t1.c について分類されることになっていた場合、 t1 には t1.c についての索引を持つ外部表としてアクセスするのが良いでしょう。 結合の出力は順序どおりになっていますが、 分類の必要はありません。
SELECT * FROM t1, t2 WHERE t1.a = t2.b ORDER BY t1.c
マージ結合においては、内部表と外部表は 1 回しか読み取られないため、 表にアクセスする順序はさほど重要ではありません。 しかし、外部表の重複結合値に対応する内部表の部分は、 メモリー内バッファーに保持されます。 外部表の次の行が外部表の直前の行と同じである場合にはバッファーは再読み取りされ、 それ以外の場合にはバッファーはリセットされます。 重複結合値の数がメモリー内バッファーの容量を超えた場合には、 すべての重複が保持されるわけではなくなります。 これは、ある値の重複が多くあり、 その値と一致する値が外部表にある場合にのみ起こります。
重複値に関するこれらの考慮事項をすべて考えたうえで、多くの場合、 重複が少ない表の方が結合における外部表として選択されます。 しかし最終的には、 最適化プログラムは詳細なコスト見積もりに基づいて外部表と内部表を選択します。
最適化プログラムは、さまざまな探索方式を使用して、 最適な結合方法を判別します。 使用される探索方式は、 使用中の最適化クラスによって決まります (最適化クラスの調整を参照してください)。 探索方式とその特性は以下のとおりです。
結合列挙アルゴリズムは、 最適化プログラムが探索するプランの組み合わせの数の主要な決定要素です。
一般に、照会で参照される表は、結合述部によって接続されていなければなりません。 結合述部を使わないで 2 つの表が結合していると、 2 つの表のカルテシアン積が形成されます。 つまり、最初の表の該当する各行が 2 番目の該当する各行に結合され、 2 つの表のサイズの乗積から成る、通常は非常に大きい結果表が作成されます。 そのようなプランがうまく実行するとは考えられないため、 最適化プログラムはそうしたアクセス・プランのコストの判別でさえも行いません。 唯一の例外として行われるのは、 最適化クラスが 9 に設定される場合、 または"スタースキーマ"という特殊な場合です。 詳細については、 最適化クラスの調整を参照してください。
カルテシアン積を含むアクセス・プランがうまく実行されるのは、通常、 スタースキーマ技法で設計された大規模な意思決定支援データベースです。 スタースキーマとは大量の生データが多数の列を持つ 1 つの表に保持されたデータベース設計のことで、 一般に "ファクト" 表として知られています。 多数の列には、ファクト表に格納された特定のデータの次元を特徴付けるコード化値が入っています。 ファクトのサブセットの一部を容易に分析できるように、 寸法表を用いてコード化値をデコードします。 一般的な照会は寸法表のデコード値を参照する複数のローカル述部で成っており、 次元表をファクト表に接続する結合述部が含まれています。 このような照会では、複数の小さい寸法表のカルテシアン積を実行してから、 大きいファクト表にアクセスするのが役に立ちます。 この技法は、複数の結合述部を複数列索引に突き合わせる場合に役に立ちます。
DB2 には、少なくとも 2 つの寸法表を持つスタースキーマによって設計されたデータベースに対する照会を認識する機能や、 寸法表のカルテシアン積の形成を含む潜在的なプランを入れる探索スペースを大きくする機能があります。 カルテシアン積を含むプランが見積もりで最低コストである場合は、 そのプランが最適化プログラムによって選択されます。
上記で説明したスタースキーマ技法は、 基本キー索引が結合内で使用されたと想定していました。 それとは別に、外部キー索引に関係するシナリオもあり得ます。 ファクト表の外部キー列が単一列索引で、 全寸法表をまたがって相対的に高度な選択が行われる場合には、 以下に示すようなスター型結合技法を使用することができます。
この技法を使用する場合、複数列索引を持つ必要はありません。 この技法を選択するために、 ファクト表と次元表の間の明示的な参照保全制約は必要ではありませんが、 ファクト表と次元表の間の関係には、この特性がなければなりません。
別の重要なパラメーターが照会中の結合の順序の形状を決定します。 一対の表を結合した結果は、複合表という新しい表になります。 一般に、こうした複合表は別の内部表との結合の外部表となります。 これを"複合外部表"と言います。 ある場合、特に貪欲型結合列挙方法を使用している場合には、 2 つの表を結合した結果を選び、それを後の結合の内部表にすると役に立ちます。 結合自体の内部表が 2 つ以上の表を結合した結果で成っていると、 そのプランには"複合内部表"が含まれているとみなします。 たとえば、次のような照会では、
SELECT COUNT(*) FROM T1, T2, T3, T4 WHERE T1.A = T2.A AND T3.A = T4.A AND T2.Z = T3.Z
表 T1 と T2 を結合し ( T1xT2 )、T3 を T4 に結合します ( T3xT4 )。 最後に、最初の結合結果を外部表として選択し、 2 番目の結合結果を内部表として選択すると役に立ちます。 最後のプランでは ((T1xT2) x (T3xT4))、 結合結果 (T3xT4) が複合内部表となります。 照会最適化クラスに従って、 最適化プログラムは結合の内部表となる最大数の表に異なる制約を課します。 複合内部表は最適化クラス 5、7、および 9 で使用できます。
区分データベース環境で複写要約表を使用すると、 基礎表データのデータベース管理事前計算済み値を持つことになるので、 パフォーマンスを向上させることができます。 たとえば、次の照会は以下の複写要約表を作成することから益を得ています。 次のような前提事項があります。
その後、EMPLOYEE 表の情報に基づき、 複写された要約表を作成します。
CREATE TABLE R_EMPLOYEE AS ( SELECT EMPNO, FIRSTNME, MIDINIT, LASTNAME, WORKDEPT FROM EMPLOYEE ) DATA INITIALLY DEFERRED REFRESH IMMEDIATE IN REGIONTABLESPACE REPLICATED;
作成後に、複写された要約表の内容は、 以下のステートメントを実行することにより更新されます。
REFRESH TABLE R_EMPLOYEE;
次の例は、売上を従業員ごと、部署の合計ごと、総合計ごとに計算しています。
SELECT d.mgrno, e.empno, SUM(s.sales) FROM department AS d, employee AS e, sales AS s WHERE s.sales_person = e.lastname AND e.workdept = d.deptno GROUP BY ROLLUP(d.mgrno, e.empno) ORDER BY d.mgrno, e.empno;
1 つのデータベース区画にしか存在しない EMPLOYEE 表を使用するのではなく、 データベース・マネージャーは、 SALES 表が存在する各データベース区画で複写される R_EMPLOYEE 表を使用します。 結合を計算するために、 従業員の情報をネットワークを超えてそれぞれのデータベース区画に移動させる必要はないので、 パフォーマンスが向上します。
以下のセクションでは、 区分データベース環境において可能な結合方式について説明します。 DB2 最適化プログラムは、各アプリケーションの要件に応じて、 最適な結合方式を自動的に選択します。 ここに記載されている結合方式を参照して、 各方式で行われていることを理解するのに役立ててください。 「表待ち行列」は、データベース区画間 (または、 単一区画データベースの場合はプロセッサー間) で行を転送するための機構です。
以下の説明では、指示 表待ち行列は、 行が受け取り側のデータベース区画のいずれか 1 つに対してハッシュされる表待ち行列のことを言います。 ブロードキャスト 表待ち行列は、 行が受け取り側のデータベース区画すべてに送られる表待ち行列のことを言います (つまり、 ハッシュはされません)。 このセクションの図では、q1、q2、q3 は、例に出てくる表待ち行列と対応しています。 同様に、図に示されている表は、これらのシナリオの目的に合わせて、 2 つのデータベース区画にまたがって分割されています。 矢印は、表待ち行列が送られる方向を示します。 なお、調整プログラム・ノードは区画 0 です。
区分データベース内で頻繁な結合が行われる表についての考慮事項の一つに、 表の併置があります。 表の併置は、区分データベースにおいて、ある表のデータを、同じ区分化キーに基づいて、 同じ区画にある別の表のデータを使用して見つけ出すための手段を提供します。 併置が行われると、照会の中で結合されるデータは、 照会作業の一部として別のデータベース区画に移動せずに処理されます。 結合の応答セットのみが調整プログラム・ノードに移動されます。 この問題の詳細は、 管理の手引き: 計画 の『表の併置』を参照してください。
結合の従属関係については、SQL 解説書 を参照してください。
最適化プログラムが併置結合を処理するためには、結合される表は併置され、 対応する区分化キーのすべての対が equijoin 述部に入れられなければなりません。 図 80 に例が示されています。
注: | 複写要約表は照合結合の可能性を高めます。 詳細については、複写要約表を参照してください。 |
![]() |
この並列結合方式は、結合される表の間に equijoin 述部がない場合に使用することができます。 また、この結合方式は、これが最も費用対効果が良い結合方式である状況でも使用されます。 典型的には、非常に大きな表が 1 つと非常に小さな表が 1 つあり、 どちらの表も結合述部列上で区分化されていない場合に使用されます。 両方の表を区分化するよりも、 小さな表を大きな表に同報通信するほうが「安く」なる可能性があります。 図 81 に例が示されています。
![]() |
この結合方式では、外部表の各行を、 (内部表の区分化属性に基づいて) 内部表のデータベース区画のいずれか 1 つに送ります。 結合は、このデータベース区画上で行われます。 図 82 に例が示されています。
![]() |
この方式では、結合を行う列の値に基づいて、 外部表および内部表の行がデータベース区画のセットに送られます。 結合は、これらのデータベース区画上で行われます。 図 83 に例が示されています。
![]() |
この方式では、内部表が外部結合表のすべてのデータベース区画に対して同報通信されます。 図 84 に例が示されています。
![]() |
この方式では、内部表の各行を、 (外部表の区分化属性に基づいて) 外部結合表のデータベース区画のいずれか 1 つに送ります。 結合は、このデータベース区画上で行われます。 図 85 に例が示されています。
![]() |
表待ち行列は、以下の目的で使用されます。
各表待ち行列はそれぞれ、単一方向にデータを渡すのに使用されます。
コンパイラーはどこで表待ち行列が必要とされているかを判断し、 それらをプランに組み込みます。 プランが実行されると、データベース区画間の接続を行うとその表待ち行列が開始されます。 表待ち行列がクローズされるのは、処理が終了したときです。
表待ち行列には、以下に示すようにいくつかの種類があります。
非同期表待ち行列は、SELECT ステートメントに FOR FETCH ONLY 文節を指定した場合に使用されます。 行の取り出しだけを行う場合には、非同期表待ち行列が他よりも速い方法になります。
同期表待ち行列は、 SELECT ステートメントに FOR FETCH ONLY 文節が指定されていない場合に使用されます。 区分データベース環境では、行の更新を行う場合には、 データベース・マネージャーは同期表待ち行列を使用します。
最適化プログラムは、アクセス・プランを選択する際に、 データの分類によるパフォーマンスの影響を考慮します。 分類は、取り出した行を要求された順序で並び替えることができる索引が存在しない場合に行われます。 また、最適化プログラムが索引走査よりも分類の方が低コストであると判断した場合にも、 分類が行われます。 最適化プログラムは、データの分類を行う際には、以下のいずれかの処置を実行します。
分類の完了時に、データの最終的な分類済みリストが 1 回の順次受け渡しで読み取り可能な場合には、 結果はパイプ処理 することができます。 パイプ処理は、分類結果を受け渡す他の (非パイプ) 手段を使用するよりも高速です。 最適化プログラムは可能ならば、分類結果をパイプ処理することを選択します。
分類がパイプ処理されるかどうかには関係なく、 分類時間は、分類する行の数、キー・サイズ、および行の幅を含め、 いくつかの要因によって違ってきます。 分類される行が、分類ヒープ内で使用可能なスペースより多くのスペースを占める場合は、 複数の分類パスが実行され、 各パスごとに行全体のうちの 1 つのサブセットが分類されることになります。 各分類パスはバッファー・プール内の一時表に記憶されます。 (バッファー・プール管理の一部として、 この一時表のページをディスクに書き込むこともできます。) すべての分類パスが完了したなら、 それらの分類済みサブセットをマージして、 分類済みの単一の行集合にする必要があります。 分類をパイプ処理する場合、行をマージするときに、 直接リレーショナル・データ・サービスに渡されます。
詳細については、分類パフォーマンス問題の標識の探索、 または 照会最適化に影響する構成パラメーターの sortheap 構成パラメーターの説明を参照してください。
場合によっては、最適化プログラムは、 リレーショナル・データ・サービス構成要素のデータ管理サービス構成要素に対して、 分類操作または集約操作の後入れ先出しを選択することができます。 これらの操作を後入れ先出しにすると、 データ管理サービス構成要素がデータを分類ルーチンまたは集約ルーチンに直接渡せるようになり、 パフォーマンスが向上します。 この後入れ先出を行わない場合、 データ管理サービスはまずこのデータをリレーショナル・データ・サービスに渡し、 次いで分類または集約ルーチンとインターフェースを取ります。 たとえば、次の照会にはこの最適化方法が適しています。
SELECT WORKDEPT, AVG(SALARY) AS AVG_DEPT_SALARY FROM EMPLOYEE GROUP BY WORKDEPT
GROUP BY 操作で必要な順序を決めるのに分類が使用されるときは、 最適化プログラムは、分類の実行中に GROUP BY の集約の一部または全部を実行することを選べます。 これは、各グループにある行の数が多い場合は有利です。 分類中に行われる何らかのグループ化により、 分類をディスクにスピルさせる必要がなくなっているか少なくなっている場合はさらに有利です。
分類の集約が行われるときには、正しい結果が計算されるようにするために、 必要な集約の段階が 3 つあります。 最初の集約の段階である "部分集約" は、 分類ヒープがいっぱいになるまで集約値を計算します。 部分集約は、非集約データが取り込まれて部分集合が作成される処理です。 分類ヒープがいっぱいになると、 残りのデータはディスクにスピルされ、 現在いっぱいになっている分類ヒープで計算された部分集合のすべてが入るようになります。 分類ヒープがリセットされた後、新しい集約が開始されます。
2 番目の集約の段階である "中間集約" は、 すべてのスピルされた分類実行を取り込んで、 さらにグループ化キーに対して集約を行います。 グループ化キー列は区分化キー列のサブセットなので、この集約は終了しません。 中間集約は、既存の部分集合を取り込んで新しい部分集合を作ります。 この段階は任意選択であり、区画内並行処理と区画間並行処理の両方に使用されます。 最後のケースでは、グローバル・グループ化キーが使用可能になるときにグループ化は終了します。 区画間並行処理では、グループ化キーが、 複数の区画にわたってグループを分けている区分化キーのサブセットであって、 集約を完了するために再区分化が必要な場合に、このことが生じます。 集約を完了するために単一のエージェントに減らされる前に、 各エージェントがスピルされた分類実行を終了するときに、 似たようなケースが区画内並行処理で存在します。
最後の集約の段階である "最終集約" は、 すべての部分集合を取り込んで集約を完了させます。 最終集約は、部分集合を取り込んで最終集合を作ります。 このステップは、GROUP BY オペレーターで常に生じます。 分類が分割されないという保証はないので、 分類が集約を完了させることはありません。 完全な集約は、非集約データを取り込んで、最終集合を作ります。 集約のこの方式は、すでに正しい順序になっているデータをグループ化するとき、 また区分化で集約の使用が禁止されていないときに、よく使用されます。