目次


DB2 における UNION ALL ビューを使用した区分化

Comments

概要

今日のリレーショナル・データベースでは、テラバイト規模のデータベースを耳にすることもめずらしくはありません。データベース内の1つの表に保管されるべきデータ量の増加で、このようなデータの扱い方についての関心が高まりつつあります。こうした状況の多くに対する解決策は、通常は分割して克服する方法です。DB2を使用する場合に一般的に推奨されるソリューションは、区分データベースの使用です。他に長期的ソリューションが必要であることは重々認識したうえで、本書では、既存の「区分化」ソリューションであるUNION ALL ビュー方式を解説します。オプティマイザーにおける UNION ALL ビューの扱いは十分に強化されており、大量でありながら単一の関係としての表示が必要なデータの管理を考えたとき、考慮する価値があります。

1. はじめに

現在使用されているビジネス・インテリジェンス・アプリケーションには、大量の履歴データの保管が伴います。一般的なアプリケーションの1つに、数年間にわたる売上データなど、過去のビジネス・トランザクションを保管して分析するアプリケーションがあります。毎日生成される500MBのデータの3年分のすべての売上トランザクションを保管する売上予測システムなどは、容易に想像できます。これには、売上データ用に全部で約500GBのデータのアクティブ・ストレージが必要になります。
DB2の初期のバージョンでは、データが保管される表スペースのページ・サイズは4KBでした。表内の個々の行は、行に位置づけるための4バイトの行識別子 (RID)を使用し、そのうち3バイトはページを識別するため、残りの1バイトはページ内のオフセットを識別するために使用されていました。したがって、ページの最大数は、3バイトに保管可能な最大整数値によって自ずと制限されました。つまり、1つの表の上限サイズは4KBページが16M個で64GBでした。DB2の後続バージョンではページ・サイズが大きくなり、ページ・サイズ32KBの表スペースを使用して、表の最大サイズは512GBに拡張されました。しかし、この上限値は前述の例には問題です。
このような表のサイズ制限を克服すると共に、並列性を通してスケーラビリティーを実現するために、DB2はシェアード・ナッシング・アーキテクチャーを採用しました。この区分データベースによって、表をいくつかのノード上で区分化できるようになりました(各ノードには表サイズ制限があります)。表のサイズは、提供できるノードの数に応じて拡張できるようになりました。ノードIDがRIDを拡張し、表により多くのデータを保管できるようになりました。ハッシュ区分化方式を使用して、表の1つ以上の列の値をハッシュ処理することにより、データはさまざまなノード間で分散されます。これがDB2で表のサイズ制限を克服するための一般的な推奨方法です。
単一区画で構成されるDB2を利用するユーザーが表の拡張を予期していなかったり、すぐには区分データベースへ移行できないような状況も考えられます。IBMは代替ソリューションの必要性を認識しており、DB2 の将来バージョンで対応を考えています。さしあったって、検討の価値がある方式が1つあります。データを単一の表に保管する代わりに、複数の表を使用します。そしてそれらの表全体にわたるUNION ALLビューを定義します。アプリケーションの照会は、このビューを参照して、すべてのコンポーネント表にあるデータを単一エンティティーとして眺めることができます。本書の目的は、この方式を解説し、この選択肢を魅力的なものにするためのDB2における開発状況について説明します。また、このソリューションの限界と、関連領域における将来の開発について解説します。
本書の構成は次のとおりです。この後のセクション2で典型的なビジネス・アプリケーションと考え得るデータベースの課題を紹介します。セクション3では、UNION ALLビュー方式を簡単に提示し、それらの課題の解決策を示します。セクション4では、DB2 SQLオプティマイザーによって実施される作業のうち、この方式で使用される作業をすべて説明します。照会が最適な方向へ変化して行く過程を用いて、各種最適化について詳細に調べます。そして最適化の最終結果を最初の照会と比較します。セクション5と6には、UNION ALLビュー方式の利点と限界をそれぞれ別々にリストし、セクション7で結論を述べます。

2. ビジネス・アプリケーションと考え得るデータベースの課題

ある世界的な商社が売上データ用にデータ・ウェアハウスを構築することを決めました。財務部門は、ある期間ごとに、全地域にわたって全商品の売上を追跡し分析することを希望しています。表の論理設計は次のとおりです。

sales( sales_date date not null,
    prod_id integer,
    city_id integer,
    channel_id integer,
    revenue decimal(20,2))

products( prod_id integer,
    prod_desc varchar(50),
    prod_group_id integer,
    prod_group_desc varchar(50),
    launch_date date,
    terminated char(1))

geographies( region_id integer,
    region varchar(50),
    country_id integer,
    country varchar(50),
    state_id char(3),
    state varchar(50),
    city_id integer,
    city varchar(50))

channel( channel_id integer,
    channel varchar(50),
    channel_cost decimal(20,2))

sales表には、3年間にわたる売上トランザクションを保管します。世界中の全売上から収集された売上トランザクションは毎日約500MBであると見積っています。product 表には製造された全商品が記録されます。geographies表は city_id を都市名、州、国、地域に対応づけます。channel表は、会社が商品販売に使用する全チャネルと、統合されたチャネル・コストを参照します。
毎日500MBの売上トランザクションがあるので、sales表は月に約15GB、年に約180GBに拡大します。単一ノードのデータベースでは、ちょうど3年分のデータで表サイズの上限に達します。何らかの理由で区分データベースへの移行が適切でない場合には、これは問題です。最初の問題は、DB2での単一ノードの限界を考えた場合、このような大量データを保管する能力です。
もう1つの問題は管理タスクを遂行する方法で、大規模な表では重要です。単一表のオフライン・バックアップには長い時間がかかります。バックアップやその他のユーティリティーのために、表を利用できない状況にユーザーを置ける保守のための時間は、限られています。IBMは保守期間のタスクを遂行しやすくする多くの機能を実装しました。IBMは常に、データの可用性を最大化するために、一層優れた管理機能の実装を計画しています。高可用性はDB2の次バージョンにおける重要な強化ポイントです。保守のための時間を最適化することが、さらに必要とされます。
表の照会パフォーマンスが懸案事項である場合もあります。大規模な表の索引は、小規模な表の索引よりももっと多くのレベルを持ちます。索引のプローブが数多くあれば、索引全体をナビゲートするための余分なI/Oが発生して最高のパフォーマンスが得られないことがあります。

3. UNION ALL ビュー方式

複数ノードの区分データベース以外で、前述のような規模の表を扱い管理タスクを管理するための実践的な方式は、sales表を一連のより小さな表に物理的に区分化することです。具体的には、列定義の同じ各表が異なる期間の売上トランザクションを表すような表一式で、sales表を表します。たとえば、1998年1月の売上トランザクションにはsales_0198表、1998年2月の売上トランザクションには sales_0298 表などという具合いです。次に、UNION ALLを使用して、すべての表をall_salesという名前のビューにまとめます。この種のビューをUNION ALL ビューと呼びます。UNION ALL ビューの分岐は、一様な構造やデータ範囲を持つ必要はありません。これにより、パフォーマンスとハードウェアの要因に基づいて完全なカスタマイゼーションを実施できます。基礎表salesが次のように分散されても問題ありません。すなわち、もっとも古い年のデータを1つの基礎表に入れ、中間の年の各四半期のデータを別の表に入れ、続いて現在の年の各月用に基礎表を1つずつ作成します。アプリケーションの変更を不要にするために、ビューをsalesと呼ぶこともできます。DB2 V7.2では、UNION ALLビュー経由のINSERTはまだサポートされていないため、ビューは、月次売上表に個別にロードされる必要があります。これはDB2の将来バージョンで対処されます。
表sales_0198が1998年1月の売上データだけを含むことを保証するために、次のように表定義に検査制約を配する必要があります。

create table sales_0198(
sales_date date not null,
prod_idinteger,
city_id integer,
channel_id integer,
revenue decimal(20,2),
constraint ck_date
	check
	 (sales_date between '01-01-1998' and '31-01-1998'))

検査制約は、関係する月次売上の表だけがアクセスされることを保証することによって、SQLオプティマイザーがall_salesビューに対する照会のパフォーマンスを改善するためにも必要です。このことはセクション4で詳述します。この方式の欠点は、この表に対する insertおよびupdateステートメントはどれでも、表に追加または表で変更される行のすべてに対して、検査制約を検証するための余分なステップを踏む必要があることです。
DB2 の将来リリースでは、「情報制約」と呼ばれる新種の制約が追加されます。この制約は、SQLオプティマイザーに表の制約に関する情報を提供するために使用されますが、挿入や削除にオーバーヘッドが生じないというものです。同じ結果を達成できるもう1つの選択肢は、代わりにUNION ALLビューのそれぞれの表のすべてにwhere文節を定義することです。この選択肢は、データが適切な表にロードされることを保証するために、表へデータをロードする前にスクリーニング処理を実施する場合に使用できます。

次のステートメントは、ビューsalesの定義を示しています。

create view all_sales as
	(
	select * from sales_0198
	 [where sales_date between '01-01-1998' and '31-01-1998']
	   union all
	select * from sales_0298
	 [where sales_date between '01-02-1998' and '28-02-1998']
	   union all
	...
	   union all
	select * from sales_1200
	 [where sales_date between '01-12-2000' and '31-12-2000']
	);

オプションのwhere文節 (識別のために角カッコ [ ] で指定) は、基礎表が売上トランザクションの日付範囲に対する検査制約を定義していない場合のみ必要です。Oracleのパーティション・ビューをご存知であれば、Oracleがなぜこの機能のサポートの停止を予定しているのか疑問に思われるかもしれません。Oracleのこの機能は、表を分割するという同じ原則に基づきますが、ビューを定義している関連構造にもっと制限があります。すべての表は類似したスキーマおよび索引を持つ必要があります。基本のUNION ALL方式が特徴とする柔軟性と独立性はありません。ここで解説したUNION ALLビュー方式を使用して得られる利点の多くは、Oracleのパーティション・ビューでは無効です。Oracleのレンジ・パーティションの可用性とOracleのパーティション・ビューの制限が理由で、サポートが停止されます。
IBMは将来的に表の断片化をさらに改善し、処理能力の問題に対処し、管理を容易にし、高可用性のためのソリューションを提供する予定ですが、その将来計画においても、IBMは引き続き UNION ALLビュー機能をサポートし強化します。この方法でビューと表を定義するにあたり、IBMに再考を強いるような具体的な事柄は一切ありません。

4. DB2 SQLオプティマイザーと UNION ALLビュー

DB2 SQLオプティマイザーは、照会を処理するうえでアクセスが必要となる表の数を減らそうとします。次の最適化は、相互に作用して区分化されたビューに対する照会のパフォーマンスを改善します。

  • ローカル述部のプッシュダウン
  • 重複する分岐の除去
  • 結合プッシュダウン
  • group by プッシュダウン

オプティマイザーの照会書き換えコンポーネントは強力な変換エンジンであり、上記最適化は照会書き換えエンジンによって実施されます。ここにリストした最適化は、UNION-ALLビューに明示的に関連したものだけで、ほとんどの照会に効果がある最適化は他にも数多くあります。
この4つの最適化の各々については、後続セクションで詳細に検証します。そこでは、最適化の利点を説明し、照会サンプルでの各最適化の結果を示し、最後に考え得る欠点を補うための手段を説明します。これらセクションを通して、前述のようなビジネス上の問題に対する照会が徐々に最適な形へと変化してゆく様子を示します。
UNION ALL照会の最適化に使われ、後続セクションで解説される変換にかかるコストは高く、したがって最適化レベル5以上で有効にされます。
会社が取得したい情報には、2000年の1月と2月に各都市で生成されたアクティブ商品ごとの全チャネル別売上合計などがあります。これは次のようなSQL 照会で表現できます。

照会 1:
select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as "Total Revenue"
  from products p, geographies g, channel c, all_sales s
  where s.prod_id = p.prod_id and
    s.city_id = g.city_id and
    s.channel_id = c.channel_id and
    s.sales_date between '01-01-2000' and '29-02-2000' and
    P.terminated = 'N'
  group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel

この照会には、前記リストの最適化方法のすべてを適用できます。後続セクションを通して、最終様式へ至るこの照会の変換過程を示します。

図 1 にこの照会を図示します。

図 1.照会 1 のグラフィック表現
図 1.照会 1 のグラフィック表現
図 1.照会 1 のグラフィック表現

4.1 ローカル述部のプッシュダウン
DB2 の SQL オプティマイザーは、select、join、union、あるいは group byを通して適格な述部をプッシュダウンします。述部プッシュダウンの目的は、操作間の中間データの流れを削減するために、早い段階で制約を適用することです。ローカル述部が最低レベルの操作までプッシュダウンできる場合、それら述部による制約は、条件を満たさない行があれば除去し、条件を満たす行だけをすぐ上のレベルの操作に供給します。
セクション4の照会例では、プッシュダウン対象として適格な3つのローカル述部があります('01-01-2000' <= s.sales_date、s.sales_date <= '29-02-2000'、およびp.terminated = 'N)。このうち s.sales_date を含む 2 つの述部は、区分化されたsales 表のそれぞれにunion-allを通してプッシュダウンされ、p.terminatedを含む述部は products 表にプッシュダウンされます。
ローカル述部のプッシュダウン後、照会は次のようになります。

照会 2:
 with p1 as (select prod_id, prod_desc from products
    where terminated = 'N'),
  s1 as (select * from sales_0198
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000'),
  s2 as (select * from sales_0298
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000'),
  ...
  s36 as (select * from sales_1200
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000'),
  sales2 as (select * from s1
  union all
select * from s2
  union all
...
select * from s36)
select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as "Total Revenue"
from p1 p, geographies g, channel c, sales2 s
where s.prod_id = p.prod_id and
  s.city_id = g.city_id and
  s.channel_id = c.channel_id
group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel

図2に示すように、述部は UNION ALL ビューの all_sales の基礎表のすべてにプッシュダウンされます。

図 2.照会 2 のグラフィック表現
図 2.照会 2 のグラフィック表現
図 2.照会 2 のグラフィック表現

述部を早く適用することの利点は、作業が早い段階で実施されることです。上記の照会では、結合に先立ち、products表からの行をフィルタリングしています。products 表に 30,000 行あり、そのうち1,000 行だけが条件 terminated='N を満たすと想定します。結合する行が少なくなるので、products表に関係する結合はより効率的になり、結合結果から多くの行を除去する必要がなくなります。

4.2 重複する分岐の除去
この最適化メソッドはローカル述部のプッシュダウンと共に作用して、照会のパフォーマンスを改善します。重複した分岐の除去は、各分岐用に設定された述部セットの中で非一貫性を検知することによって機能します。述部の特定のサブセットに一貫性がない場合、その分岐の操作から行が戻されるはずはありません。UNION ALLビューのそのような分岐のアクセスを排除してアクセス・プラン内に現れないようにすることができます。作成されたビューS1を見てみましょう。角カッコで囲まれた述部は、基礎表sales_0198で定義された検査制約です。

照会 3:
select * from sales_0198
 where '01-01-2000' <= sales_date and
   sales_date <= '29-02-2000'
    [ and '01-01-1998' <= sales_date and
      sales_date <= '31-01-1998' ]

上記 SQL ステートメント内の4つの述部のすべてを満たす行がないことを証明するのは、難しいことではありません。特に、表sales_0198に保管されたsales_dateは01-01-98未満であると同時に01-01-2000を超えることはあり得ません。いったんDB2オプティマイザーがこの非一貫性を検知すると、オプティマイザーはunion allのこの分岐のアクセスは不要であってこの照会の実行のunion allからドロップできることを認知します。セクション4.1の照会結果でいったん重複する分岐の除去が発生すると、残る照会は次のようになります。

照会 4:
with p1 as (select prod_id, prod_desc from products
    where terminated = 'N'),
  s25 as (select * from sales_0100
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000'),
  s26 as (select * from sales_0200
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000'),
  sales2 as (select * from s25
  union all
 select * from s26)
 select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as "Total Revenue"
 from p1 p, geographies g, channel c, sales2 s
 where s.prod_id = p.prod_id and
  s.city_id = g.city_id and
  s.channel_id = c.channel_id
 group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel

図 3 にこの照会を図示します。
見てのとおり、union all内の分岐の数は、36から2に減りました。表や索引のアクセスも、34個減りました。
非一貫性検知は、等価または不等価述部 (<、>、<=、>=、=、<>、between)にもっとも効果がありますが、INやORなどのより複雑な種類の述部にも効果はあります。より複雑な述部タイプでは、完全な実証には極端にコストがかかるために、実施されないことがよくあります。
述部タイプが複雑であればあるほど、実証も難しくなります。たとえば、複数のIN述部がある場合、各IN述部の要素をそれぞれ比較しなければなりません。この比較の実施はコスト高で、IN述部はほとんどの場合には一貫性があります。等価述部と1つのIN述部がある場合は例外です。このような場合、非一貫性を検知しようと全比較を実施します。

図 3.照会4のグラフィック表現
図 3.照会4のグラフィック表現
図 3.照会4のグラフィック表現

たとえば、prod_group_id列で区分化されたproducts表に対する UNION ALLビューall_productsを想定します。ビューは各基礎表に厳密に1つの商品グループが含まれるように設定され、prod_group_idに関して等価検査制約が強いられます。そして次のような照会があるとします。

照会 5:
select *
 from all_product
 where prod_group_id in (1, 3, 5)

オプティマイザーは、IN述部内の要素のいずれかに一致するprod_group_idを持つものを除いて、すべての基礎表へのアクセスを除去します。
UPPER(state) = 'ONTARIO' などの関数を含む述部は、UNION-ALLビューの分岐に一貫性がないことを証明するために使用することはできません。例外はYEARとMONTH関数です。たとえば、述部YEAR(sales_date)=2000が指定された場合、'01-01-2000'<= sales_date and sales_date < '01-01-2001' に変換されます。同様に述部YEAR(sales_date)=2000 and MONTH(sales_date)=2は '01-02-2000' <= sales_date and sales_date < '01-03-2000' に変換されます。date関数と共にIN 述部かOR述部を使用とすると、プルーニングの促進に失敗します。
検査制約の照会が UNION-ALL ビューの区分列で関数を使用する場合、これが唯一の課題です。この解決策は、区分列として生成された列を使用することです。たとえば、区分列としてstate列を使用して、geographiesに上からUNION-ALLビューが定義される必要があり、エラーを避ける必要のある場合、UNION-ALLビューの分岐の選択にstateの記述の大文字表記が使用されます。通常、述部内にUPPER 関数があるために、分岐の除去は起こりません。その代わりに、次のように、生成された列を使用して各表を定義します。

create table geographies_1(
  
  region_id integer,
  region varchar(50),
  country_id integer,
  country varchar(50),
  state_id char(3),
  state varchar(50),
  state_up generated always as (UPPER(state)),
  city_id integer,
  city varchar(50))

state_up列は常にstateの大文字表記を含み、この列を使用すると分岐の除去を起こすことができます。
DB2 バージョン7では、常にアクセス・プランから分岐を除去できるわけではありません。非一貫性が実証されても除去されない分岐は実行時に除去され、アクセスされません。この点については、セクション7.2 で解説します。

4.3 結合プッシュダウン
UNION ALL ビューについて、DB2 SQL オプティマイザーは、基礎表への結合プッシュダウンを試みます。これにより、結合操作をもっと効率的にするために、基礎表に索引があれば使用できることを保証します。結合プッシュダウンは、上位操作へ流れ込む行数を減らすことができるので、ローカル述部のプッシュダウンと同じ効果があります。結合プッシュダウンは等結合と、UNION ALLビュー内の残りの分岐の数が36未満のときだけに限定されます。この制限は、DB2の将来バージョンで変更されます。結合プッシュダウンは重複する分岐の除去の後で適用されるため、UNION ALLビューの分岐の中にはすでに除去されたものもあります。
セクション4の例に戻ると、結合プッシュダウンの後の照会は次のようになります。

照会 6:
with s25 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
        from sales_0100 s, products p, geographies g, channel c
        where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
          s.prod_id = p.prod_id and
          s.city_id = g.city_id and
          s.channel_id = c.channel_id and
          p. terminated = 'N'),
      s26 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
        from sales_0200 s, products p, geographies g, channel c
        where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and

          s.prod_id = p.prod_id and
          s.city_id = g.city_id and
          s.channel_id = c.channel_id and
          p. terminated = 'N'),
        sales2 as (select * from s25
        union all
        select * from s26)
        select prod_id, prod_desc, city, channel, sum(revenue) as "Total Revenue" from sales2
          group by prod_id, prod_desc, city_id, city, channel_id, channel
図 4 にこれを示します。
図 4 にこれを示します。
図 4 にこれを示します。

図 4.照会6のグラフィック表現

結合はそれぞれのsales基礎表で行われています。次のような理由で、この方がより効率的です。

  1. sales_0100 および sales_0200 表は、all_sales UNION ALL ビューよりも行数が少ない。
  2. sales 区画に対する索引はどれでも使用できるようになったため、利用可能な結合メソッドの選択範囲が広がった。

結合プッシュダウンは通常は優れた最適化ですが、オプティマイザーが可能な限り対応している欠点もいくつかあります。このような欠点には、コンパイル時間と複雑なサブコンポーネントのレプリケーションがあります。
結合プッシュダウンを使用すると、照会のコンパイル時間が長くなることがあります。プッシュダウンされる結合構造が非常に複雑な場合は、特に長くなります。前記の例では結合の数は倍増しています。これはすなわち、オプティマイザーがUNION ALLビューのそれぞれの分岐について個別に最高の結合順序を決定する必要があることを意味します。この単純な例では問題ではないかもしれませんが、何百という結合のある状況では、コンパイル時間は飛躍的に増加してしまいます。
UNION ALL ビューの各分岐へ結合構造をレプリケーションすることは、プッシュダウンされる結合構造が非常に複雑な場合、照会のメモリー要件が増える原因となります。これは、各分岐が同一の結合構造の独自コピーを使用するためです。DB2の将来バージョンで全分岐が共通コピーを使用できるようになる予定です。
構造が複雑すぎるとオプティマイザーが判断した場合にプッシュダウンを阻止できるように制限が設けられています。さらに、外部結合は、状況によっては可能であるにもかかわらず、DB2の現行バージョンではプッシュダウンされません。
1つの照会内で複数の UNION ALL ビューが結合されている場合、オプティマイザーは1つのUNION ALLビューを他の視点を通してプッシュダウンすることも考慮します。geographies表がregion列で区分化されたUNION ALLビューであった場合、結合プッシュダウンの結果は、非常に異なったはずです。照会7はその様子を示しています。便宜上、geographiesのUNION ALLビューの分岐は3つだけとします。

照会 7:
 with s251 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0100 s, products p, geographies_1 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  s261 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0200 s, products p, geographies_1 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  s252 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0100 s, products p, geographies_2 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  s262 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0200 s, products p, geographies_2 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  s253 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0100 s, products p, geographies_3 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  s263 as (select s.prod_id, p.prod_desc, g.city, c.channel, s.revenue
    from sales_0200 s, products p, geographies_3 g, channel c
    where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id and
      p. terminated = 'N'),
  g1 as (select * from s251
          union all
        select * from s261),
  g2 as (select * from s252
          union all
        select * from s262),
  g3 as (select * from s253
          union all
        select * from s263),
  sales2 as (select * from g1
          union all
        select * from g2
          union all
        select * from g3)
  select prod_id, prod_desc, city, channel, sum(revenue) as "Total Revenue"
  from sales2
  group by prod_id, prod_desc, city_id, city, channel_id, channel

今度は結合はunionの下で実施されています。前と同様に各結合の入力セットはより小さくなり、基礎表の索引も利用できます。
通常の表結合プッシュダウンと同様に、プッシュダウンを考慮する際に、UNION ALLビューのサイズ制限があります。現行の制限は、union結合プッシュダウンを通したunionは、180を超える結合を作成する場合には発生しないというものです。たとえば、16分岐の UNION ALLビューと結合された12分岐の UNION ALL ビューがある場合、その結合は192個の結合を作成するため、プッシュダウンされません。

4.4 Group By プッシュダウン
この内部変換では、グループ化の操作が早い段階でできるだけ小規模な行セットに適用できるように、UNION ALLビューを通してGROUP BYをプッシュダウンしようとします。GROUP BYをプッシュダウンすることにより、大規模なソートの実施を回避でき、順序を提供する索引があれば活用できます。Group Byプッシュダウンは区分列でグループ化する場合に特に効果的ですが、ほとんどすべての場合に適用できます。Group Byプッシュダウンが実行されるのは、すべてのグループ化関数がMIN、MAX、SUM、COUNT、あるいはAVGのいずれかで、区画除去後の残りの分岐の数が36未満であるという、特定の状況のみです。この制限は、DB2の将来バージョンで変更される場合もあります。

例の続きで、group byプッシュダウン後に、照会は次のような形になります。

照会 8:
 with s25 as (select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as totrev
      from sales_0100 s, products p, geographies g, channel c
      where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
        s.prod_id = p.prod_id and
        s.city_id = g.city_id and
        s.channel_id = c.channel_id and
        p.terminated = 'N'
      group by s.prod_id, p.prod_desc, s.city_id, g.city, c.channel_id, c.channel),
    s26 as (select s.prod_id, p.prod_desc, g.city, c.channel, (s.revenue) as totrev
      from sales_0200 s, products p, geographies g, channel c
      where '01-01-2000' <= sales_date and sales_date <= '29-02-2000' and
        s.prod_id = p.prod_id and
        s.city_id = g.city_id and
        s.channel_id = c.channel_id and
        p.terminated = 'N'
      group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel),
    sales2 as (select * from s25
                  union all
            select * from s26)
select prod_id, prod_desc, city, channel, sum(totrev) as "Total Revenue"
  from sales2
  group by prod_id, prod_desc, city_id, city, channel_id, channel

この変換済み照会 (照会 8) を図示すると、図5のようになります。

図 5 .照会8のグラフィック表現
図 5 .照会8のグラフィック表現
図 5 .照会8のグラフィック表現

グループ化は UNION ALL操作の前に小さなデータの集合で早期に実施されるので、この照会は、より効率的です。つまり、グループ化を実施するためにソートが必要になるデータの集合がより小さいことを意味します。選択された結合メソッドがその結果をソート順に出力する場合、ソートは一切不要になることもあります。
下位のGROUP BYの結果を1つにまとめるために、UNION ALLビューより上に別のグループ化が必要になります。ただし、この追加のgroup by は、グループ化がすでに統合済みの行で行われるため、非常に効率的です。この例では、union allより上には、1つにまとめるための最上位のグループ化操作用に、1つのグループあたり2行だけ (すなわち、unionの各分岐から1行ずつ) があるだけです。
group byプッシュダウンの欠点は結合プッシュダウンの欠点と同じで、コンパイル時間と複雑な構造のレプリケーションです。結合プッシュダウンと同様に、group by副構造が複雑すぎる場合にプッシュダウンを回避するために、オプティマイザーでは制限を設けています。

4.5 最適化の結果
以上の最適化がすべて適用されると、照会は広範囲に変更されます。最適化はすべて、早い段階で実施されるほど、後で処理するデータ・セットのサイズが小さくなります。次の2つの図はこれを表しています。最初の図6は元の照会を示し、次の図7は前セクションで示した最終的な照会を示しています。各操作の上の数字は、操作が返す推定行数です。
図からわかるように、書き換え済みの照会では、各操作の入力の集合が元の照会よりも小さくなっています。このため、各操作の効率が上がり、したがって照会全体の効率も向上します。
DB2 内の最適化レベル設定機能は、DB2オプティマイザーで使用されるさまざまな照会変換や最適化戦略を制御する手段です。UNION ALL ビューを使用する照会を最適化するには、多くの処理が必要で、多くのメモリーが使用されます。このため、このような最適化のほとんどは、最適化レベル5以上のみで実施されます。

図 6.元の照会の各操作における推定行数
図 6.元の照会の各操作における推定行数
図 6.元の照会の各操作における推定行数

前述の最適化がすべて実施されると、照会は次のように実行されます。

図 7.最適化された照会の各操作におけるsuitei行数
図 7.最適化された照会の各操作におけるsuitei行数
図 7.最適化された照会の各操作におけるsuitei行数

5. Union-All ビューの利点

照会パフォーマンスに効果があるほか、区分化されたビューには、その他多くの利点があります。たとえば次のような利点です。

  • 保守期間のユーティリティー制御の改善
  • 容易なデータ・ロール・イン/ロール・アウト
  • Tivoli の HSM と「ニアライン (オンラインに限りなく近い) ストレージ」のUNION

ALL ビュー

  • 分岐に基づくパフォーマンス・チューニング
  • スキーマとデータの進化
  • 分岐除去を通して削減される I/O
  • 並列性の向上

5.1 保守時間のユーティリティーの制御の改善
大規模な表に Union-All ビューを使用すると、バックアップおよびリカバリーのパフォーマンスを改善できます。1つの大規模な表では、バックアップのために表をオフラインにする期間は、比例的に大きくなります。表のサイズによっては、短い保守時間に管理するには大きすぎる場合もあります。UNION ALLビュー方式では、バックアップが必要なのは最新の分岐だけで、その他の分岐はすべて、変わることのない履歴データを包含しています。複数の分岐のバックアップが必要な場合、各分岐のバックアップは独立したバックアップ装置を使用して実施できるので、バックアップの並列処理が可能です。
同様に、リカバリーも並列に実施でき、データは各表にロードされ索引も再構築されます。その他の保守作業の大半にも同様な好影響があります。索引作成と統計情報収集は、個々の表での完了時間が短くなり、処理は並列化できるため、かかる時間も短くなります。
新たにロール・インされたデータに対する RUNSTATS などのユーティリティーは、古いデータの統計情報を収集する負荷もかからずに実行できます。

5.2 容易なデータ・ロール・イン/ロール・アウト
これまでの例に沿って、1つの業務を例にとって説明します。四半期ごとに、システム管理者は、新しい四半期用にスペースを確保するために月間データを古いものから3ヶ月分ロール・アウトする必要があります。
単一表ソリューションでは、行の削除と表の再編成が必要です。前述の例では、sales 表には5億4000万行があり、1四半期のロール・アウトには4500万行を削除する必要がありますが、必要なロギング時間も考えると機能しません。エラーが発生すると本番の表が使えなくなってしまうため、ロギングを使用不可にするわけには行きません。
UNION-ALLビューソリューションでは、新四半期の3ヶ月の表を作成してビューを再定義します。ビューが再定義されると、ビジネス要件に応じて、ロール・アウトされた四半期の表はアーカイブされるか削除されます。
区画の細分性がロール・イン/ロール・アウトされるデータより粗い場合、単に表をドロップして新しい表をビューに追加すればいいほど簡単ではないように思えます。しかしUNION ALLビューに WHERE 述部があれば、次のような制御も可能です。
新規データのロール・インには、データが表に挿入された後でビューを変更する。
古いデータのロール・アウトには、データが表から削除される前にビューを変更する。
ビューが変更されたときに初めて、照会は変更を認識します。

5.3 TivoliのHSMと「ニアライン・ストレージ」の UNION ALL 視点
UNION ALL ビューを (Oracle のレンジ・パーティションを含む) その他すべての区分化方式から差別化する重要な適用例の1つは、多様なメディア・タイプを効果的に活用できる機能、たとえば Tivoliの Hierarchical Storage Manager などです。個々の区画間は相対的に独立しているので、表を別々の表スペースに定義できます。このため、照会頻度の低い区画をテープやその他の遅いメディア上に保管することもできます。
UNION ALL ビューを使えばアーカイブ・データのアクセスは簡単です。セクション4に示した最適化を使用すれば、アクセスする必要があるのは、適切なデータを含むアーカイブ可能なメディアだけです。このアーカイブ・データの使用頻度は非常に低いので、テープなどの遅いメディアに保管しても問題ありません。

5.4 分岐に基づくパフォーマンス・チューニング
単一表のソリューションでは、パフォーマンスを改善するために表全体が同じ方法でチューニングされます。索引はすべてのデータにわたって一様に定義されます。統計情報は同時に更新されます。
UNION ALLビュー方式では、UNIONの分岐からの各表には、独立した索引を定義できます。さらに、統計情報は別々に収集でき、変更された情報だけを更新できます。使用頻度の高い表には、使用頻度の低い表より多くの索引を定義したり、高速なメディアに保管することもできます。これにより、パフォーマンスおよびスペースの問題を扱う際に、システム管理者に途方もなく高い柔軟性が提供されます。
UNION ALLビューによって管理される表の数を削減するために、データをスキューする柔軟性もあります。つまり、大規模な表に古い表を含め、比較的小さな表に新規データを含めることもできます。各UNION ALL分岐は別々に最適化されるので、大きな表をアクセスする際には、小さな表をアクセスする場合とは異なるアクセス・プランを持つことができます。

5.5 スキーマとデータの進化
ビジネス・ニーズを満たすデータの進化は、UNION ALLビューを使って簡単に実現できます。たとえば、前記で定義されたビジネス上の問題を使用して、販売部門が2001年2月の売上を3つの異なる部門別に分類したいとすると、ビューは次のように再定義されます。

sales_0201( sales_date date not null,
    Prod_id integer,
    city_id integer,
    channel_id integer,
    revenue_dept1 decimal(20,2),
    revenue_dept2 decimal(20,2),
    revenue_dept3 decimal(20,2))

課題は、データを新しいスキーマにどのように適合させるかです。単一表のソリューションでは、新規列を含めるには表全体の再定義が必要です。履歴データに対しては、その新規列に何を入れるかも問題です。
UNION ALLビューソリューションでは答えはもっと簡単で、次の2つのうちのいずれかの方法でビューを再定義できます。
ソリューション 1: all_sales ビュースキーマには変更なく、アプリケーションにもおそらく変更はありません。

create view all_sales as
( select sales_date, prod_id, city_id, channel_id, revenue from sales_0398
  union all
select sales_date, prod_id, city_id, channel_id, revenue from sales_0498
  union all
...
  union all
select sales_date, prod_id, city_id, channel_id,
  revenue_dept1+revenue_dept2+revenue_dept3

  from sales_0201
);

ソリューション 2: all_sales ビュースキーマは新規列を取り込みます。

create view all_sales (sales_date, prod_id, city_id, channel_id, revenue,
                     revenue_dep1, revenue_dept2, revenue_dept3)
as
( select sales_date, prod_id, city_id, channel_id, revenue, NULL, NULL, NULL
from sales_0398
  union all
select sales_date, prod_id, city_id, channel_id, revenue, NULL, NULL, NULL
  from sales_0498
  union all
...
  union all
select sales_date, prod_id, city_id, channel_id,
                revenue_dept1+revenue_dept2+revenue_dept3,
  revenue_dept1, revenue_dept2, revenue_dept3
  from sales_0201
)
;

スキーマは新規要件を満たすように変更されました。最初は古いビュースキーマに留まる方が効果的です。数ヶ月が過ぎて新しいスキーマを使用するデータ量が増えたときに、スキーマが変更された新しいビューに切り換えることができます。こうすると、変更されるビューに依存するアプリケーションがあれば、それらにとっても時間的な余裕ができます。上記の例では、いったん全アプリケーションが変更された後、ビューの売上合計の列をドロップできます。

5.6分岐除去を通して削減されるI/O
セクション 4.2 では、union-allビューに対する照会でオプティマイザーが実施する重複分岐の除去について解説しました。アクセスされる分岐の数の削減は重要な利点ですが、その他のI/O 操作の削減ももう1つの利点です。典型的なビジネス・ニーズには、2つの四半期間(2001年第1四半期と 2000年第2四半期など) の売上実績比較があります。
セクション2のビジネス・アプリケーションと課題の項で説明したように、大量の売上履歴データに単一表を用いるソリューションでは、sales_date列で索引走査を実施して各四半期の行を取り出し、続いて表をアクセスして残りのデータを取り出すというアクセス・プランがよく選択されます。表に5億4000万行があり、9000万行だけが照会に必要であるとすると、索引走査は検出した索引エントリーのほとんどを破棄します。
union-allビュー方式では、分岐の除去が起きた後、適切なデータを含む 6 つの表だけが、照会の一部として残ります。これらの表の定義によって、必要なデータのすべてがそれらに含まれていることが分かります。オプティマイザーは索引を完全に避けて、これらの表でシーケンシャルな表走査を実施できるようになります。これでI/O コストを大幅に節減できます。

5.7 並列性の向上
I/O コストの削減に加えて、Union-Allビューは DB2 EEEの並列性の向上につながります。セクション5.6の例で、照会は2つの四半期 (2001年第1四半期と2000年第1四半期) の売上実績の比較を試みました。分岐の除去で、無関係なすべてのI/Oを削除しました。しかし、2つの表走査をなおも逐次的に実行しました。IBM DB2 EEEでは、複数の「ノード」にわたって処理を並列化できます。各ノードは他のノードと並列に操作を実行し、「ハッシュ」区分化方式を使用して、ノード間でデータを分散します。
大量データを扱うための Union-All ビュー方式は、EEE 環境で使用されるハッシュ区分化と円滑に統合します。Union-Allビューは「範囲の」区分化と「ハッシュ」区分化の混在を定義でき、これによって、データをunion-all ビュー経由で範囲に区分化でき、基礎表をデータベースのノード全体に分散できます。EEEのこの2レベルの区分化には、複数の表をアクセスする照会を並列にアクセスできるという効果があります。
上記の例で、これが EEE データベースであって、基礎表が異なるノード間に分散されたとすると、6つの表の表走査は並列に実行でき、最後にユーザーに対する出力用に組み合わせます。セクション4に記述した、DB2 SQLオプティマイザーが実施する最適化のすべては、この並列処理の恩恵を受けられます。グループ化と結合プッシュダウンにより、さまざまな操作を別々のノード上で実施できます。最後の組み合わせ以外の全操作のコストは、アクセスされる全ノード間で分割されます。

6. UNION ALL ビュー方式の制限事項

UNION ALL ビュー方式では、いくつか重要な制限事項があります。これら制限事項の中には、DB2の将来バージョンで対処されるものもありますが、ソリューションとしてこの方式を選択する前に、次のような制限事項を十分検討することが重要です。

  • グローバルなユニーク索引の制限
  • UNION ALL ビューへの INSERT
  • UNION ALL ビューの作成時の分岐数制限
  • コンパイル時間とメモリー使用量の増加

6.1 グローバルなユニーク索引の制限
UNION ALL ビュー方式の制限事項の1つは、1つの表全体にわたってグローバルにユニーク索引を作成できないことです。例題の売上データでは、各トランザクションにユニークな番号が付与されたと想定すると、索引を使用してUNION-ALLビューのすべての分岐にわたるグローバルな一意性を保証することはできません。これを保証するには別の方法を使用する必要があります。たとえば、データベース全体で一意であることが保証されるSEQUENCE 値を使用するなどの方法です。
グローバルなユニーク索引を持てないと、特定の場合のパフォーマンスの問題につながることがあります。各トランザクションにユニークID が与えられている場合、ID 順に全トランザクションのリストが欲しければ、ソートを実施する必要があります。グローバルなユニーク索引を使用すると、トランザクションは索引から順々に読み取られます。さらに、このようなID列は、それが主キーとなる参照保全性の関係はサポートされません。

6.2 UNION ALL ビューへの INSERT
現行バージョンのDB2 (V7) には、UNION ALLビュー経由でINSERTができないという制限事項があります。INSERTはUNION ALLビューで使用される個別の表へ直接実施される必要があります。ただし、これは一時的な制限事項です。DB2のバージョン8では、all_salesビュー経由でデータを直接ロードできるようになり、どの月次sales表へデータを挿入すべきかをDB2エンジンが決定するようになります。DB2エンジンは、表定義で検査制約を使用して適切な表を判断します。

6.3 UNION ALLビューの作成時の分岐数制限
CREATE VIEW ステートメントの実行中、ビューの意味と構文の妥当性を検査する作業が必要になります。これには、各表に関連付けられたすべての情報を読み取ることと、ビューの定義に使われたステートメントのコンパイル時と同様に列タイプを解決することが含まれます。UNION ALLビューの各分岐は、独立した表を参照します。関係する表は同じ列数、同じタイプ、そしてよく似た統計情報を持つこともあります。しかし、DB2はこれらを独立した表としてみなし、コンパイル中にこの情報を保管するためにメモリーを消費します。したがって、ビュー内に定義できる表の数に制限があるのは、歓迎されることです。簡単なテストでは、表数の上限値は大体400程度であることを示しています。この数字は、表の複雑性や統計情報の収集範囲には無関係です。

6.4 コンパイル時間とメモリー使用量の増加
多数の分岐を持つUNION ALLビューの場合、セクション4に記述した最適化の実施には、多くの作業が必要です。さらに、オプティマイザーは各分岐を見て、各分岐に最適なアクセス・プランを求めて個別の最適化を実施します。これは、すべての変換が実施された後の照会の複雑性に応じて、コンパイル時間が長くなることを示唆します。UNIONを通した結合プッシュダウンを使用すると、メモリー要件は急増します。DB2 コンパイラーによって使用されるメモリー割り当てに関係したSTMTHEAP データベース構成パラメーターのサイズを増やすことを推奨します。
UNION ALLビューの分岐が除去されない場合、オプティマイザーによって選択されたプランの構築および実行時のメモリー要件も増加します。STMTHEAPの他に、APPLHEAPSZデータベース構成パラメーターも増やす必要があります。簡単なテストでは、これら2つの構成パラメーターの最大値を適用すると、使用できるUNION ALLビューのサイズが制限されます。
一般的に、UNION ALL分岐の数を適度な数に制限することをお勧めします。DB2 V7.2では36未満に、コンパイル時間が問題であればそれより小さな数にすると良いでしょう。
UNION-ALLビューに関係する分岐の数の理論上の上限値は、ビューをアクセスする任意の照会の他のコンポーネントによって異なりますが、100~200
です。分岐数の上限に近づきつつある UNION-ALLビューを使用すると、照会の実行で得られるパフォーマンスは最適状態には及びません。

7. 将来の機能拡張

UNION-ALL方式は、大量データを扱うための長期的ソリューションになるように機能改善が図られています。次に、このような機能改善のいくつかの簡単な説明を説明します。

  • 分岐除去機能の改善
  • パラメーター・マーカーとホスト変数を使用した分岐除去
  • UNION-ALL ビュー経由の DML
  • 複雑な構造の影響の削減

7.1 分岐除去機能の改善
セクション4.2では、重複する分岐の除去と、照会に対して実施した場合のその改善効果を解説しました。重複する分岐の除去は、述部集合内の非一貫性を検知するオプティマイザーの機能に依存します。
IN述部を使用した分岐の除去は、現行リリースでは非常に制限されています。IN述部を使用して発生する唯一の非一貫性検知は、等価述部を使用したときです。セクション4.2でproducts表の例を説明しました。この例では、区画を用いて UNION-ALLビューを定義し、等価検査制約を使用しました。
DB2 V7 Fixpak 6では、照会で、ビューとIN述部の定義に範囲述部の検査制約を使用する機能が追加されています。さて、salesデータのビジネス上の問題の例に戻ります。店舗の日曜日の開店を続けるべきかどうかを会社が見極めたいとします。これは、日曜日の売上合計として表すことができます。通常、適切な行のフィルタリングにはIN 述部が使用されます (sales_date in ('07-01-2001','14-01-2001','21-01-2001','28-01-2001'))。しかし、現行のUNION-ALLの分岐除去機能ではこれを扱うことができません。Fixpak6では、唯一必要な表であるsales_0101を除く全分岐が除去されるように、対応されます。
DB2 V8 では、相互に対して複数のIN述部を証明する機能が追加されます。たとえば、products表がUNION-ALLビューを使用して prod_group_id列で区分化されているとします。ビューは、たとえばprod_group_id in (1, 3, 4, 5) など、IN述部のとおりに検査制約付きで定義されます。productsのunion-allビューに対する照会は、IN述部 prod_group_id in (1,3,5) を使用することもできます。バージョン8では、分岐除去が発生して不要な全分岐が削除されます。
分岐除去に対しては、他にも機能改善が行われています。

7.2 パラメーター・マーカーとホスト変数を使用した分岐除去
セクション4.2に示したとおり、分岐または区画の除去は、照会に指定された値と、表やビュー述部の制約に基づいて実施されます。コンパイル時にパラメーター・マーカーやホスト変数の値をアクセスすることはできません。
実行時に、ホスト変数の値とパラメーター・マーカーのどちらに基づいて分岐を実行すべきかの評価が行われます。現時点では、実行時の分岐除去は、UNION-ALLビュー方式とは互換性がありません。それは、基礎表に対する検査制約と、ビュー定義内のwhere文節に対する検査制約の両方を定義する必要があるためです。
次のステートメントは、DB2バージョン7で、実行時の分岐除去を可能にするために必要なビューsalesの定義を示しています。

create view all_sales as
    (
    select * from sales_0198
    where sales_date between '01-01-1998' and '31-01-1998'
      union all

    select * from sales_0298
    where sales_date between '01-02-1998' and '28-02-1998'
      union all
    ...
      union all
    select * from sales_1200
    where sales_date between '01-12-2000' and '31-12-2000'
    );

where文節は、同じことを定義する検査制約と同様に必要になります。DB2 バージョン8では、検査制約かwhere文節のどちらか一方だけが必要になります。
実行時の分岐除去の例を見てみましょう。セクション4の例を使用して、次のような照会を開始します。ただし、範囲述部にはホスト変数使用します。

照会 9:
select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as "Total Revenue"
from products p, geographies g, channel c, all_sales s
where s.prod_id = p.prod_id and
  s.city_id = g.city_id and
  s.channel_id = c.channel_id and
  s.sales_date between :hv_dt1 and :hv_dt2 and
  P.terminated = 'N'
group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel

セクション4で解説した最適化のすべてが適用されます。ただし、値:hv_dt1と:hv_dt2は未知であるため、重複する分岐の除去は何ら進みません。すべての最適化が適用された後、照会は次のようになります。

照会 10:
with p1 as (select prod_id, prod_desc from products
    where terminated = 'N'),
  p2 as (select prod_id, prod_desc from products
    where terminated = 'N'),
  s1 as (select s.prod_id, p.prod_desc, g.city, c.channel, sum(s.revenue) as totrev
    from sales_0198 s, p1 p, geographies g, channel c
    where :hv_dt1 <= sales_date and sales_date <= :hv_dt2 and
       [ '01-01-1998 <= sales_date and sales_date <= '01-31-1998' and]
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id
    group by s.prod_id, p.prod_desc, s.city_id, g.city, c.channel_id, c.channel),
  ...
  s36 as (select s.prod_id, p.prod_desc, g.city, c.channel, (s.revenue) as totrev
    from sales_1200 s, p2 p, geographies g, channel c
    where :hv_dt1 <= sales_date and sales_date <= :hv_dt2 and
       ['01-12-2000' <= sales_date and sales_date <= '31-12-2000' and]
      s.prod_id = p.prod_id and
      s.city_id = g.city_id and
      s.channel_id = c.channel_id
    group by s.prod_id, p.prod_desc, s.city_id, g.city, s.channel_id, c.channel),
  sales2 as (select * from s1
        union all
      select * from s2
      union all
      ...
      select * from s26)
select prod_id, prod_desc, city, channel, sum(totrev) as "Total Revenue"
from sales2
group by prod_id, prod_desc, city_id, city, channel_id, channel

各基礎表の検査制約は、追加述部として角カッコで囲んで示しています。s1 述部から、:hv_dt1<= '31-01-1998' and '01-01-1998' <= :hv_dt2 という述部が容易に得られます。DB2バージョン8では、この種の述部は、実行時に表や索引のアクセスが発生する前に評価されます。実行時に:hv_dt1 = '01-02-1998' and :hv_dt2 = '31-03-1998' の場合、得られる述部は'01-02-1998' <= '31-01-1998' and '01-01-1998' <= '31-03-1998 になります。最初のものは明らかに誤りで、分岐はコンパイル時に除去されなかったとしても、アクセスされません。
実行時の分岐除去の様式はV7では制限されており、基礎表での検査制約とビュー内のwhere文節の両方として、区分化制約を定義する必要があります。
実行時の分岐除去は、実際には照会が実行されるまで除去が発生しない場合でも、オプティマイザーのプラン内に見て取れます。オプティマイザーの典型的なプランでは、次のようになります。

			0.507407
             NLJOIN
              ( 5)
            31.9794
               1
        /------+-----\
0.037037           13.7
TBSCAN             TBSCAN
( 6)                  ( 7)
0.0491778          31.9302
     0                 1
     |                 |
     1                137
TABFNC: SYSIBM  TABLE: SALES
GENROW           SALES_0198

これはどこで実行時の分岐除去が発生するかを示唆しています。左側の表関数SYSIBM.GENROW からの分岐が、実行時の分岐除去の発生する場所です。次のものが最初に評価されます。

   7) Residual Predicate
                   Relational Operator:             Less Than or Equal (<=)
                   Subquery Input Required:         No
                   Filter Factor:                    0.333333
                   Predicate Text:
                   --------------
                   (:HV_DT1 <= 31-01-1998)

この述部 :hv_dt1 <= '31-01-1998', が最初に評価され、false ならば表 sales.sales_0198はアクセスされません。

7.3 UNION-ALL ビュー経由の DML
DB2 バージョン 7 では、データは union-all ビューへ直接挿入できません。アプリケーションで基礎表のそれぞれに手動で挿入する必要があります。バージョン8では、この制約が緩和されます。union-allビューを通常の表として使用して、INSERT、UPDATE、DELETE操作を実施できるようになります。

7.4 複雑な構造の影響の削減
セクション 4.3 と 4.4 で、グループ化および結合構造のプッシュダウンを説明しました。構造が非常に複雑な場合、メモリー要件とコンパイル時間が余計に必要となるため、グループ化と結合操作はプッシュダウンされません。
UNION-ALLビューの分岐がこの複雑な構造の共通コピーを使用できるように、機能改善が図られています。これにより、複雑な構造は
union-allビューの各分岐用に複製されないのでメモリーを節約でき、また、共通コピー用に策定されるアクセス・プランは1つだけで済むためにコンパイル時間を節減できます。

8. 結論

本書では分割して課題を克服する UNION ALL ビュー方式を解説し、検討の価値のあるソリューションを示しました。

  • 容量の問題を克服する
  • 短い保守時間の問題に対処する
  • 実行時間を改善する

DB2はDB2オプティマイザーの一部としての非常に強力な照会書き換えコンポーネントを備えています。このコンポーネントによって、これらの課題のいくつかの解決に向けた効果的なソリューションとして、簡単なUNION ALLビューの使用が可能になります。このソリューションはこれらの課題すべてに効く万能薬ではなく、制限事項もあります。本書には一時的な制限事項のいくつかも解説しました。あらゆるシナリオに完璧なソリューションではありませんが、要件に適合する場合には、本書を意思決定に役立ててください。
UNION ALL ビュー方式とともにTivoliのHSMを使用した、「ニアライン・ストレージ」という重要で風変わりな用語は、大量の履歴データを持つデータベースに対して、さまざまなアクセスおよびスピード特性を持つストレージを活用する希有な機会を提供します。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management
ArticleID=326725
ArticleTitle=DB2 における UNION ALL ビューを使用した区分化
publish-date=04072006