パーソナルツール
現在の場所: ホーム ブログ Categories DB2
« 2018January »
Su Mo Tu We Th Fr Sa
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
このBlogについて
 代表の向井田ことMUKAです。当ブログサイトでは、MAGICやDB2に関する技術者向け情報を公開しています。お気軽にお立ち寄り下さい。
最近のエントリ
twitter / 新RT機能を英語モードで・・・ muka 2009年11月19日
twitter / List Widget muka 2009年11月03日
twitter API / Retweet の仕様が・・・ muka 2009年10月30日
twitter API / Lists muka 2009年10月24日
IBM Rational Software Conference 2009 と アジャイル開発 muka 2009年10月08日
twitter API / geoタグ! muka 2009年10月01日
Twitter Developers Meetup in Tokyo muka 2009年09月11日
Club DB2 2009/9/4 muka 2009年09月05日
最近のコメント
Re:Club DB2 2009/8/29 muka 2009年09月01日
Re:Club DB2 2009/8/29 SIM 2009年08月31日
最近のトラックバック
Club DB2 8/29の感想エントリと今後の予定 Unofficial DB2 BLOG 2009年09月01日
カテゴリ
misc (47)
dbMAGIC (47)
DB2 (47)
mail (47)
Web (47)
twitter (47)
 

DB2

一つ上に移動
IBMのハイブリッドデータベースDB2 9(デービー・ツー・ナイン)に関するもの

MAGIC + DB2 で XQuery(6)


索引の作成


 ゴールデン・ウイーク真っ只中ですが、皆さん如何お過ごしでしょうか?当方も、あさってあたりボツボツと家族でハイキングなどに出かけようかと思っておるところです・・・。(はっきりとした予定もなく、気の向くままですけれど・・・(^^;)
 さて、あっという間に4月も終わってしまいましたが、その間、IBMさんの技術セミナーにも何度か参加させて頂きました。
 個人的には、DB2の奥の深さに驚くと共に、徐々に愛着が湧きつつあります。なんとか、作成するアプリケーションで生かしたいと思っているところです。

 さて、前回の終わりで紹介させて頂いたXMLデータベースの索引の件ですが、パフォーマンスを考えると、作成方法を知っておく必要があるようです。
 索引を作成するためには、ちょっと面倒なSQL文を作成しないといけないのですが、DB2の基本管理ツール「コントロールセンター」を利用すると、ウィザード的に処理を行うことが可能です。

 下の画面は、コントロールセンターから索引の作成ウィザードを起動したものです。(「DBXML」データベースを選択し、表「XMLDBTEST」をクリック→コンテキストメニューから「作成」→「索引」を選択します。)
 取り敢えず、格納されているデータがサンプルとして表示されます。このXMLデータは、おそらく「メインプログラム」(最初のプログラム)のものだと思われます。たまたま格納したプログラム中に、コールプログラムの定義があったので、そのノードを選んでみました。

v10db2_041.jpg


 上の画面で選んでいる箇所のXPATHは、下記のようになります。

/Application
/ProgramsRepository
/Programs
/Task
/TaskLogic
/LogicUnit
/LogicLines
/LogicLine
/CallTask
/OperationType
/@val

 ウィザードを実行すると「CREATE INDEX」文を作成してくれます。
 しかし、このとき作成されるXPathは、なかなかピッタリしたものにはならないところが玉に瑕(たまにきず)です。

CONNECT TO DB2XML;
CREATE INDEX MUKAIDA.VAL ON DB2ADMIN.XMLDBTEST(DATAXML)
GENERATE KEY USING XMLPATTERN
'//@val' AS SQL VARCHAR(817)
PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS
PAGE SPLIT SYMMETRIC COLLECT STATISTICS ;
CONNECT RESET;
上の構文の中で、「//@val」で表現されてしまっているのが問題のXPathです。本来ならここは「//CallTask/OperationType/@val」としたいところです。(V10のソースの中では、「@val」は至るところに出現します。一般論として「//」のようなXPathの指定は避けられるなら避けたほうが良いらしいのですが、MAGIC V10では、タスク(/Task)は再帰的に出現するので避けることができません。ちなみに、VARCHAR(817)はインデックスのサイズらしいのですが、この再帰的なタスク構造の深さを考慮して、もっと大きい値にすべきかもしれません。)
 そこで、一旦、メモ帳などに貼り付けておいて、下記のように修正しましょう。
 プログラムのリファレンスを検索する場合は、3つのXPathでANDを取る必要があるので、索引は3つ(TEST_IDX1,TEST_IDX2,TEST_IDX3)作成してみました。
CONNECT TO DB2XML;
CREATE INDEX DB2ADMIN.TEST_IDX1 ON DB2ADMIN.XMLDBTEST(DATAXML)
GENERATE KEY USING XMLPATTERN
'//CallTask/OperationType/@val' AS SQL VARCHAR(817)
PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS
PAGE SPLIT SYMMETRIC COLLECT STATISTICS ;
CREATE INDEX DB2ADMIN.TEST_IDX2 ON DB2ADMIN.XMLDBTEST(DATAXML)
GENERATE KEY USING XMLPATTERN
'//CallTask/TaskID/@comp' AS SQL VARCHAR(817)
PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS
PAGE SPLIT SYMMETRIC COLLECT STATISTICS ;
CREATE INDEX DB2ADMIN.TEST_IDX3 ON DB2ADMIN.XMLDBTEST(DATAXML)
GENERATE KEY USING XMLPATTERN
'//CallTask/TaskID/@obj' AS SQL VARCHAR(817)
PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS
PAGE SPLIT SYMMETRIC COLLECT STATISTICS ;
CONNECT RESET;
このように、ウィザードの結果を参考にしながら、修正を加えて使っていけば結構便利かも知れません。


アクセスプランによる索引の検証


 DB2 9には、オプティマイザというものが実装されています。これは、SQL文を処理する際に、使用すべき最適な索引を自動的に選択してくれるものです。
 コマンドエディタを使うと実際に実行されたアクセスプランを確認することが可能です。

 索引を作成する前に実行したアクセスプランが次のものです。

v10db2_042.jpg

 索引を作成した後で実行したアクセスプランは次のように変わります。

v10db2_043.jpg

 この例では、作成した索引(TEST_IDX1~3)が使用され、パフォーマンスも良くなっていることが分かります。
 ただ、問題はどこにどのように索引を付けるか・・・です。
 今回は、実行するSQL文に合わせて索引を作成しましたから、「正しく索引を付ければパフォーマンスが上がる」ことを検証したに過ぎません。
 このあたりの問題は、DB2 9のオートノミックな機能に任せるべきところ、アプリケーションとしてチューニングを必要とするところ、それぞれの見極めが必要でしょう。

p.s
 今回は、なんかMAGICの話が出てきませんでしたが、ごめんなさい。
 いろいろと話題を準備しておりますので、楽しみにしていて下さい。
 最新版のMAGIC V10(10.1 SP2)で出力したサンプルプログラムをアップしておきます。

 <改良点>
  1.   「プログラム一覧」のコンテキストメニューに「XMLインポート」を追加し、表示中のプログラムソースを一括してDB2にインポートできるようにしました。
  2. プログラム14に、「データベースと表の作成」プログラムを追加しました。これにより、使用するDB2のデータベースと表を一括して作成します。結果はコマンドプロンプト内に表示します。








MAGIC + DB2 で XQuery(1) MAGIC + DB2 で XQuery(1)
サイズ 4320 - File type text/html
MAGIC + DB2 で XQuery(2) MAGIC + DB2 で XQuery(2)
サイズ 7143 - File type text/html
MAGIC + DB2 で XQuery(3) MAGIC + DB2 で XQuery(3)
サイズ 10488 - File type text/html
MAGIC + DB2 で XQuery(4) MAGIC + DB2 で XQuery(4)
サイズ 14638 - File type text/html
MAGIC + DB2 で XQuery(5) MAGIC + DB2 で XQuery(5)
サイズ 7537 - File type text/html
V10HACKS_20070502.lzh V10HACKS_20070502.lzh
サイズ 111.6 kB - File type application/x-lzh
カテゴリ
dbMAGIC
DB2

MAGIC + DB2 で XQuery(7)


ユーザ定義関数の利用


 皆様、ゴールデンウイークは如何でしたでしょうか?
 こちらは、家族サービスで江ノ島に行ってきたんですが、予想通り、すごい人でした。食事は2時間近く待たされたり、息子とはぐれてしまったり・・・。
 まぁ、それなりには楽しめましたが・・・。

 さて、前回は、DB2オンリーの話題でしたので、今回はMAGIC中心でいきます。
 「ユーザ定義関数」は、もう皆さんお使いでしょうか?
 V10で新しく追加された機能の中でも、今までのMAGICの常識を覆す機能という意味で、かなり「目玉」じゃないかと個人的には思っております。
 何が凄いのかというのは、MAGICを知ってる人でないと判らないところが哀しいところですが、要は、自分の作ったプログラムを関数にできるということです。(他の言語系の開発ツールを使っている人なら、「何を当たり前じゃないの」と言われてしまいそう・・・)
 

 例えば、XMLドキュメントの任意のノードからテキストを取得する場合はどういうコーディングになるでしょうか?
 XMLGETという関数があるじゃない?と言われてしまいそうですが、これが難点なのは「入出力テーブルを定義して介さないと使えない」という点です。
 加えて言うなら、「親子二つのタスクを定義する必要がある」ということです。
 つまり、この関数を利用するには、入出力ファイルを定義した親タスクと、XMLGET関数を実行する子タスクの二つのタスクを作らなければなりません。
 もし、この2点が不要になったらどうでしょうか?

 例えば、DB2用に下記のような表を作成しました。(第2回

CREATE TABLE DB2ADMIN.XMLDBTEST(DATAID CHAR(20) NOT NULL,
DATAXML XML,CONSTRAINT DATAID PRIMARY KEY(DATAID))
このDATAXMLカラムに格納したXMLドキュメント(プログラムリポジトリのファイル-Prg_????.xml-を入れることを想定している)から、プログラム名やプログラムの日付を並べてテーブル表示しようとする場合は、メモリテーブルなどに予めそれらのカラムを定義しておいて、一時的なテーブルを作成するかも知れません。
 あるいは、いろいろと悩んだあげく、上記の「CREATE TABEL」文を修正して、プログラム名やその他のカラムを別個に追加することを考えるかもしれませんね。

 しかし、もし「任意のXMLドキュメントの任意のXPathのテキストを取得する関数」みたいなものがあれば、ワーク用のテーブルも子タスクも不要になります。
 しかも、手続き型の呼び出し処理ではなく、関数ですから、どこでも使えるわけです。
 これがすなわち、「ユーザ定義関数」です。


プログラミングの実際


 今回もソースを公開していますので、コーディングの詳細については、データをダウンロードして下さい。


 準備するのは、1本のプログラムです。
 取り敢えず、「XMLノード値取得」という名前で作成しましょう。
 下記のパラメータを親タスクに定義します。
#
パラメータ名称

書式 説明
1
XMLオブジェクト BLOB
入力パラメータ
2 XPath UNICODE 1000 入力パラメータ
属性を指定する場合は、
「Path/@Atr」のように指定することを想定
3 TEXT UNICODE 1000
出力パラメータ
2番目のパラメータ「XPath」は属性名も一緒に指定できるようにしましたが、MAGICの関数に合わせて、2個のパラメータに予め分割してもOKです。1個にする場合は、分割した値をセットするための変数等を定義しておきます。
 入出力テーブルに1のオブジェクトを「V=項目」で定義します。

 子タスクを作成します。

v10db2_051.jpg

 パラメータ3を「XMLGet」関数で更新します。
XMLGet (1,1,[XPathの項目],[属性名の項目])

 作成したプログラムを今度は、メインプログラムに登録します。

v10db2_052.jpg

 「ロジック」タブを開き、「F=関数」を定義します。関数名は「XMLGetNodeText」としました。
 スコープを「G=グローバル」とするとコンポーネントとして作成することにより、他のプロジェクトでも利用できるようになります。
 項目を追加し、2個(XMLObject, XPATH)はパラメータ、1個(NodeText)は変数にします。作成したプログラムをコールして、それぞれをパラメータに割り当てます。
 最後に、関数の「戻り値」に、3個目に定義した変数「NodeText」を返してあげればOKです。
 作成した関数を、データビューの「代入」等で使う場合を考慮し、2個のパラメータがいずれもNULLでないときに動作するよう、条件を定義します。


作成した関数の利用方法



 今度は、作成した関数を利用する場合のコーディング方法です。
 DB2の表に格納したプログラムリポジトリの名称と、編集時刻、コメントをテーブルで表示させてみましょう。
 メインソースに「XMLDBTEST」を定義し、DATAIDとDATAXMLを配置します。

v10db2_053.jpg

 変数を、6個(Description、Comment、_date、_time、修正日付、修正時刻)定義し、それらの代入式に次のものをセットすればOKです。

C/V
NO
S
名称
TYPE
PIC
代入
C
1
F
DATAID
A
20

C
2
G
DATAXML
B


V
1
H
Description
A
30
1
V
2
I
Comment
A
1000
2
V
3
J
_date
A
10
3
V
4
K
_time
A
10
4
V
5
L
修正日付
D
YYYY/MM/DDZ
5
V
6
M
修正時刻
T
HH:MM:SSZ
6

 代入した式は次の通りです。
#
式定義
1 XMLGetNodeText (G,'/Application/ProgramsRepository/Programs
/Task/Header/@Description')
2 XMLGetNodeText (G,'/Application/ProgramsRepository/Programs
/Task/Header/Comment/@val')
3 XMLGetNodeText (G,'/Application/ProgramsRepository/Programs
/Task/Header/LastModified/@_date')
4 XMLGetNodeText (G,'/Application/ProgramsRepository/Programs
/Task/Header/LastModified/@_time')
5 IF(J=' ',0,Val(Trim(J),'6'))
6 IF(K=' ',0,Val(Trim(K),'6'))

 下図は、フォームの編集画面です。

v10db2_054.jpg

 それでは、作成したプログラムを実行させてみましょう。
 データビューの定義のみで、DB2に格納されたXMLドキュメントから任意のテキストを取得することができました。

v10db2_055.jpg



ユーザ定義関数の活用について


 このユーザ定義関数は、ちょっとした工夫で効果絶大だと思います。
 似たような処理を共通化し、それをコンポーネントに関数として実装させれば、利用度も上がります。
アプリケーション開発も、これによって大きく変わる可能性・・・を秘めています。

 実は現在、メールデータを管理するアプリケーションの開発を行っているのですが、どうしても厄介なのがメールの日付の処理です。その部分を、このユーザ定義関数に実装しました。
 下の表は、ある一通のメール中に記載された日付文字列の例ですが、関数を定義して使うことによりコーディングをすっきりさせることができました。
 値の大小を比較できるデータに置き換えることが可能です。
 関数ですから、いろいろな場面で使えますし・・・。

メールヘッダに記載された日付
関数の戻り値
変換日付
変換時刻
Thu, 1 Mar 2007 21:46:30 -0700 (MST) 20070302044630 07/03/02
04:46:30
Thu, 1 Mar 2007 23:46:31 -0500
20070302044631
07/03/02 04:46:31
Fri, 2 Mar 2007 13:46:35 +0900 (JST)
20070302044635
07/03/02 04:46:35
Fri, 02 Mar 2007 13:46:37 +0900 20070302044637 07/03/02 04:46:37

 ということで、ちょっとV10の紹介が中心的話題となりましたが、如何でしたでしょうか?
 この機能は、是非実際に試してみられることをお勧めします。



MAGIC + DB2 で XQuery(1) MAGIC + DB2 で XQuery(1)
サイズ 4320 - File type text/html
MAGIC + DB2 で XQuery(2) MAGIC + DB2 で XQuery(2)
サイズ 7143 - File type text/html
MAGIC + DB2 で XQuery(3) MAGIC + DB2 で XQuery(3)
サイズ 10488 - File type text/html
MAGIC + DB2 で XQuery(4) MAGIC + DB2 で XQuery(4)
サイズ 14638 - File type text/html
MAGIC + DB2 で XQuery(5) MAGIC + DB2 で XQuery(5)
サイズ 7537 - File type text/html
MAGIC + DB2 で XQuery(6) MAGIC + DB2 で XQuery(6)
サイズ 7699 - File type text/html
V10HACKS_20070509.lzh V10HACKS_20070509.lzh
サイズ 124.0 kB - File type application/x-lzh
カテゴリ
dbMAGIC
DB2

MAGIC + DB2 で XQuery(8)


再度XQuery・・・



 梅雨の鬱陶しい季節ですが、皆さん如何お過ごしでしょうか?
 さて、私のほうは、この何ヶ月にわたり受託開発していたシステムの納品も済んで、少し落ち着いているところでしょうか?

 ところで、このブログもなかなかこまめに投稿できず、かなり不定期的連載となっています。(申し訳ありません)
 思えば、前回から一月も経ってしまいました。でも今回はご安心下さい。すでに書ききれないので、近日「続き」を書かないといけなくなりました。w

 ところで、この連載の始まった頃からの課題といいますか、XMLデータベースの使い方の中で、「これは、どうすればいいんだ・・・?」と疑問に思いながら追求していたことが幾つかあったんですが、そのうち一番大きいやつがこのほど、霧が晴れたように解決してしまいました。
 まぁ、この連載も手探りでやっていますので、どこに行き着くかも分からないんですが、それがまた楽しいのかも・・・w
 ということで、今回は、再びXQueryがテーマです。(正確には「今回から」=「次回も・・・」?)


「RM互換」のタスクの検索・・・


 たまたまテーマにしてるのが、MAGIC V10のソースの解析なんですが、プログラムのソース(=XMLドキュメント)をXMLデータベースに格納することにより、効率良く該当ドキュメントを検索することが可能になることは、今までの連載の中で述べてきたとおりです。
 ここで、その要点をまとめておきます。

  • XMLドキュメントは、XMLデータベース(DB2 9)のXMLタイプのカラムにそのまま格納することができる
  • 該当するドキュメントは、SQLを拡張したSQL/XMLを使用することにより検索が可能
  • 具体的には、where句でxmlexist関数を使い、XPathで指定する

 最近、某MLで、「RM互換のタスクをどうやって探せばいいか?」という話題になりました。
 「RM互換」というのは、古いバージョンのコーディング方法をサポートするために、MAGIC V10で実装された機能?で、純粋なイベントドリブンコーディングにするためには、取り除くことが望ましいとされているものです。
 私は思わずその問いの答えとして、「XMLデータベースを使って検索すれば・・・」という回答を投げたんですが、かろうじてシスオペの方に反応して頂くに留まりました。(DB2 9をいじっているMAGICの技術者はまだ少ないと思うので・・・。いや、はたして、いるのかどうか?)
 まぁ、機会ある度にDB2 9の宣伝・普及活動はしようと思っていますので、半分目的は遂げられたんですが・・・。w

 さて、この「RM互換」という機能は、良く出来ているというか、不思議な機能というか、普通のV10のコーディングであれば作成できないんですが、隠しスイッチ(MAGIC.INIに記載)で入力できるようになります。

[MAGIC_SPECIALS]SpecialRMCompatibleLogic = Y

 「ロジックテーブル」に1つだけ挿入が可能で、挿入すると自動的にデータビューの内容(パラメータ、変数、メインソースやリンクしたテーブルのカラム)が自動的に展開されます。そして、その中に項目更新やズームなどの従来レコードメインというロジックテーブルに記載していた内容を記録できるようになります。下の図は、ブロックとコールタスクを挿入した例です。

v10db2_061.jpg

 しかし、面白いのは画面上とソース上では、記載される場所が違うという点です。
 つまり、RM互換に記載したロジックは、「LogicUnit/Level/@va」lが"R"でかつ、「LogicUnit/Type@val」が"M"の「TaskLogic」(いわゆるレコードメイン)に記載されます。

v10db2_062.jpg

 これに対して、ロジックテーブルのRM互換そのもの(「LogicUnit/Level/@va」lが"M"で、「LogicUnit/Type@val」は存在しない)は、ごくわずかなその属性値が記載されるだけです。その記載の有無が問題となるのではないかと推測します。

v10db2_063.jpg

 さて、ということで、「RM互換のあるプログラムの検索」を行うためにはどうすれば良いか?という問題に戻ります。
 下のSQL文がその答えです。

select c.DATAID from DB2ADMIN.XMLDBTEST c where 
xmlexists('$i/Application//LogicUnit/Level[@val="M"]'
 passing c.DATAXML as "i")

 上記のコマンド中、「Application//LogicUnit」とある、「//」はどういう意味でしょうか?
 これは、任意の階層のタグを現すXPathの表現です。
 MAGICのプログラムのソースが悩ましいのは、タスクの記述が再帰的に記載されることです。
 ルートタスクは、「Application/ProgramsRepository/Programs/Task」ですが、孫タスクは、「Application/ProgramsRepository/Programs/Task/Task/Task」です。でも子供が3人いて、孫は全部で10人かも知れません。
 「LogicUnit」は「Task」の子の「TaskLogic」の下に作成されますが、「Application//LogicUnit」は、全ての任意のタスクの下にある「LogicUnit」を意味します。
 もしかしたら、途中を省略せずに「Application//Task/TaskLogic/LogicUnit」にしたほうが良いのかもしれません。
 「Level[@val="M"]」は、属性値が「M」である「Level」ノードを意味します。この"[" "]"で条件を特定させます。
 ここまでは、おさらいですね。
ところで、「RM互換」タスクが同じプログラム中に複数個存在したとき、それを検索するにはどうすれば良いでしょうか?当然、大きなプログラムであれば、何十というタスクが存在するかも知れません。
 上のSQL文では、プログラム(ドキュメント)のある行(レコード)は返ってきますが、それがどこのタスクにあるのかまでは分かりません。

 実は、これが、私の当初からの悩みだったのです!

 で、ちょっと前までは、検索されたドキュメント(=プログラムソース)をゴリゴリと分析することを考えていました。
 ところが、もっとスマートな方法があったんですね・・・。

(ということで、次回につづく・・・)


DB2 Star Festival 2007


 来週の月曜日に、IBMさん主催のイベントがありますね。(私も参加します!)

 去年の「Viper Night」に参加したのがDB2 9との出会いでしたが、今度のイベントでもまた「新しい出会い」があるといいな・・・と楽しみにしています。



v10db2_064.jpg


 写真(右)は、その案内状です・・・。プリマベーラって・・・?(ぐぐったらありました。(^^;)
 写真(左)に写っているのは、英文の書籍「DB2 9 : pureXML」。(いろいろと参考になりました。斜め読みですが...(^^;)
 いずれも前回の「クラブDB2」(ナイトスクール)で頂いたものです。(いつも感謝してます!)



MAGIC + DB2 で XQuery(1) MAGIC + DB2 で XQuery(1)
サイズ 4320 - File type text/html
MAGIC + DB2 で XQuery(2) MAGIC + DB2 で XQuery(2)
サイズ 7143 - File type text/html
MAGIC + DB2 で XQuery(3) MAGIC + DB2 で XQuery(3)
サイズ 10488 - File type text/html
MAGIC + DB2 で XQuery(4) MAGIC + DB2 で XQuery(4)
サイズ 14638 - File type text/html
MAGIC + DB2 で XQuery(5) MAGIC + DB2 で XQuery(5)
サイズ 7537 - File type text/html
MAGIC + DB2 で XQuery(6) MAGIC + DB2 で XQuery(6)
サイズ 7699 - File type text/html
MAGIC + DB2 で XQuery(7) MAGIC + DB2 で XQuery(7)
サイズ 12220 - File type text/html
カテゴリ
dbMAGIC
DB2

MAGIC + DB2 で XQuery(9)




DB2 Star Festival 2007


 昨日、IBMさんのイベントに行ってまいりました。
 どちらかと言えば、技術指向の内容ではないのがちょっと物足りないところもありましたが、IBMさんのDB2にかける思いが伝わるようなイベントではなかったかと思います。

 「IOD-インフォメーション・オンデマンド」という言葉は初めて聞くものでしたが、なんでも「アプリケーションと密接に結びついたデータのあり方を、必要なアプリケーションが必要に応じて必要なデータにアクセスできる環境を提供する」ということだそうです。
 聞けば当たり前のことのように思えますが、まだまだコンピュータシステムはそういうレベルまで到達できていないんでしょう。それを(XMLとDB2 9で)実現することが、日本の活力に繋がる・・・というようなお話があり、とても印象に残りました。
 懇親会も、楽しめましたし、とても良かったと思います。(プリマベーラのファンになりました。w)


再帰的に出現するノードの取得


 前回で問題となった「ドキュメント内の該当タスクの情報を取得するためにはどうしたらよいか」というのが今回の主テーマです。

 さて第4回の中で紹介したように、DB2 9では、xmlquery関数を使うことにより、カラムの情報を取得するのと同様に、任意のXQuery検索結果を取得することが可能です。

 第4回の記事では、プログラムの名称(Header/@Description)などの情報を取得するために、「Application/ProgramsRepository/Programs/Task/Header」というノードを指定していました。
select c.DATAID, 
xmlquery('$i/Application/ProgramsRepository/Programs/Task/Header'
passing c.DATAXML as "i")

from DB2ADMIN.XMLDBTEST c
where xmlexists('$i/Application//LogicUnit/Level[@val="M"]'
passing c.DATAXML as "i")
上記は、絶対パス指定で、プログラムの名称を取得することが可能です。
 さてここで、xmlquery関数のXPATHに対し、xmlexistで指定したものと同じ条件のものを指定するとどうなるでしょうか?
xmlquery('$i/Application//LogicUnit/Level[@val="M"]' 
passing c.DATAXML as "i")
DB2のコントロールセンターを使って結果を試してみます。

v10db2_071.jpg

 上記の画面では、3つのドキュメント(プログラム)が該当しています。
 XMLドキュメントの内容を確認すると、どうやら該当箇所そのものが返っているようです。

v10db2_072.jpg


 検索された場所は、「Level」ノードそのものであることが分かります。今度は、そこから相対的にパスをたどって、「Task」ノードを指定できることを確認してみましょう。
xmlquery('$i/Application//LogicUnit/Level[@val="M"]/../../../Header' 
passing c.DATAXML as "i")
 下図にソースの内容を示しますが、「Level」ノードからたどり、相対的なパスから「Task」ノードを探すことができるわけです。(実際は、そこから「Header」ノードに下がる...)
 タスクの「ISN_2」属性は、同一プログラム内では、ユニークに割り当てられている番号なので、この番号を取得することにより、タスクを特定することが可能です。

v10db2_077.jpg

 XPATHの後ろに「../../../Header」を追加しただけですが、これは「Level」ノードを起点として、「親の親の親の子のHeader」ノードという意味になります。
 文書ビュアーで正しく情報が取得できているかどうかを確認してみましょう。


v10db2_074.jpg

 どうやら探そうと思っていたタスクの情報をGetすることができたようですね!
 ちなみに、先のXPATH式は、起点を「Level」から「LogicUnit」にずらし、下記のような記述にも置き換えることも可能です。(上がり下がりが少ないほうがコーディング時の間違いは減る?)
xmlquery('$i/Application//LogicUnit[Level/@val="M"]/../../Header' 
passing c.DATAXML as "i")

 ところで、検索結果のXMLのうちいくつかは、正しくツリーが表示されないというエラーになります。

v10db2_075.jpg

 さて、これはどうしてでしょうか?
 このソースを表示させてみましょう。
<?xml version="1.0" encoding="UTF-16" ?>
<Header Description="ツリー表示" ISN_2="3" LastIsn="33" id="76">
<ExecutionRight comp="-1"/>
<Resident val="Y"/>
<SQL val="N"/>
<ReturnValue>
<ParametersAttributes/>
</ReturnValue>
<SqlWhereExist val="N"/>
<TaskType val="O"/>
<External val="N"/>
<LastModified _date="732797" _time="32413" date="2007/05/01"
time="09:00:13"/>
</Header>
<Header Description="ビュー項目" ISN_2="4" LastIsn="33" id="76">
<ExecutionRight comp="-1"/>
<Resident val="Y"/>
<SQL val="N"/>
<ReturnValue>
<ParametersAttributes/>
</ReturnValue>
<SqlWhereExist val="N"/>
<TaskType val="O"/>
<External val="N"/>
<LastModified _date="732702" _time="32361" date="2007/01/26"
time="08:59:21"/>
</Header>
<Header Description="グローバルパラメータ" ISN_2="14" LastIsn="33" id="76">
<ExecutionRight comp="-1"/>
<Resident val="N"/>
<SQL val="N"/>
<ReturnValue>
<ParametersAttributes/>
</ReturnValue>
<SqlWhereExist val="N"/>
<TaskType val="O"/>
<External val="N"/>
<LastModified _date="732689" _time="56122" date="2007/01/13"
time="15:35:22"/>
</Header>
(以下略)
どうやら「Header」ノードが何回も出現していることがわかります。
 これは、該当するXMLノードが複数件存在していることによるものです。ということは、タスク情報をドキュメント(=プログラムソース)内から全て探してくれているわけです。
(XMLツリー文書で見ようとしたときにエラーとなるのは、単にルートのノードが無いためです。)

 ひとまず、これで、文書内の再帰的に出現するタスクの情報も、Task/Headerノードを相対的に指定することにより、検索できることが判りました。(^^)v


出力結果の整形は、FLOWRで・・・


 XQueryには、強力な整形機能があります。すっきりした形に整えてみましょう。
 まず、さしあたって必要なのは、「Header」ノードの「Description」属性と、「ISN_2」という属性なので、これだけを取得するようにします。
 「for」文で指定した変数「$e」に、「in ~」で指定したノードが格納され、「return」文で整形して出力することが可能です。このように、「for」、「let」、「order by」、「where」、「return」の先頭文字をとって「フラワー」と呼ぶそうです。
xmlquery(
'for $e
in $i/Application//LogicUnit[Level/@val="M"]/../../Header
return <Data>{$e/@Description,$e/@ISN_2}</Data>'
passing c.DATAXML as "i")
出力結果は次のようになります。
<?xml version="1.0" encoding="UTF-16" ?>
<Data Description="ツリー表示" ISN_2="3"/>
<Data Description="ビュー項目" ISN_2="4"/>
<Data Description="グローバルパラメータ" ISN_2="14"/>
(以下略)
どうでしょうか。取得した属性は、「Data」ノードの属性に入れ替わりました。

ルートノードを追加するには、下記のように修正します。
xmlquery('
<Datas>{
for $e
in $i/Application//LogicUnit[Level/@val="M"]/../../Header
return <Data>{$e/@Description,$e/@ISN_2}</Data>
}</Datas>'
passing c.DATAXML as "i")
今度は、該当箇所が複数検索された場合でも、正しいXML文書の形で処理ができるようになりました。

v10db2_076.jpg



 ここで、おさらいしておきましょう。

  • 該当するドキュメントの検索は、XMLカラムに関しては、Where句のxmlexist関数で行う(※1)
  • ドキュメント内のノードの検索は、Select句のxmlquery関数で行う
  • xmlquery関数は、整形機能を使うことにより、いろいろな形式に出力を操作できる(※2)

 ※1  「Net Search Extender」を使う場合は、若干変わります。
 ※2 整形のみならず、絞り込んだり並べ替えも可能です。



MAGICでの結果表示


 MAGICのXML連携機能を使用し、DB2から返ったタスク情報をテーブルに展開してみましょう。
 xmlqueryのレイアウトに合わせてスキーマを定義しておきます。

v10db2_078.jpg

 作成したスキーマを「定義取得」で指定し、データリポジトリを定義します。

v10db2_079.jpg

プログラム上では、パラメータで受け取るXML結果を、「XMLソース項目」で指定することが可能です。

v10db2_080.jpg

 実行結果の画面も、検索結果のタスク一覧(左)と連動させて、タスクツリー(右)の該当箇所をハイライト表示するように改良してみました。

v10db2_081.jpg

 ちょっと最後は駆け足でしたので、例によってプログラムソースを公開しておきます。
 なお、第5回で作成したクロスリファレンスのプログラムも、同じように該当タスクの一覧を表示できるように改良してあります。
 このプログラムも、だいぶ、バージョンアップしましたね...。

 現在ベータ公開中の、「MAGIC Decrypter for V10」のほうも、誠意改良中です。 今月中には、新しいバージョンも公開できるのではないかと思いますので、またベータ評価など、宜しくお願い致します。

p.s
 ソースに添付してあるキャビネットファイルは、昨日公開された 「MAGIC eDeveloper Ver 10.1 SP2a」で作成してあります。


MAGIC + DB2 で XQuery(1) MAGIC + DB2 で XQuery(1)
サイズ 4320 - File type text/html
MAGIC + DB2 で XQuery(2) MAGIC + DB2 で XQuery(2)
サイズ 7143 - File type text/html
MAGIC + DB2 で XQuery(3) MAGIC + DB2 で XQuery(3)
サイズ 10488 - File type text/html
MAGIC + DB2 で XQuery(4) MAGIC + DB2 で XQuery(4)
サイズ 14638 - File type text/html
MAGIC + DB2 で XQuery(5) MAGIC + DB2 で XQuery(5)
サイズ 7537 - File type text/html
MAGIC + DB2 で XQuery(6) MAGIC + DB2 で XQuery(6)
サイズ 7699 - File type text/html
MAGIC + DB2 で XQuery(7) MAGIC + DB2 で XQuery(7)
サイズ 12220 - File type text/html
MAGIC + DB2 で XQuery(8) MAGIC + DB2 で XQuery(8)
サイズ 9523 - File type text/html
V10HACKS_20070710.lzh V10HACKS_20070710.lzh
サイズ 142.0 kB - File type application/x-lzh
カテゴリ
dbMAGIC
DB2

DB2 9 を利用したメール・アーカイブ・システムの開発 (1)




はじめに


 9月になり、当社も設立1周年を迎えました。
 これもひとえに、皆様のお陰と感謝しております。
 思えばがむしゃらにやってきたこの一年でしたが、二期目は今まで以上に楽しくやっていけたらと思っております。

 さて、この何ヶ月か密かに新製品の開発を行っているのですが、それが「DB2 9を利用したメール・アーカイブ・システム」です。
 現在、10月のリリースに向けて準備中という段階です。そこで、このページを借りて、その紹介などもしていきたいと思っています。
 今まで私がやってたことを知っている人からはよく、「何故、メールアーカイブなの?」と聞かれるんですが、およそ次のような理由です。

  • メールサーバ製品の開発を行っている会社との協業を考えた。
  • とにかく、「DB2 9」で何か作りたかった。
  • 内部統制などのジャンルで、今後需要の増すことが期待されている?
  • 大掛かりなシステムではないので、小さく早く始められる?
いずれにしても、全く開発経験の無いシステムだったんですが、試行錯誤しながらやってきて、ようやく形が見えてきたという感じですね。
 特に、メールレシーバや、メールデータのXML変換プログラム(後述)は、協業している会社の技術協力を頂くことができたのが大きなポイントで、これらの技術協力を頂けなければ、この製品も完成できなかったことと思います。
 また、「DB2 9」は、他のページでもいろいろ紹介させて頂いていますが、今のところ「我が社の開発テーマ」みたいなものになっています。このメールアーカイブでも大活躍します。特に、NSE(テキスト検索用エンジン、「Net Search Extender」の略)という拡張モジュールは、新たなDB2の世界を見せてくれたという感じです。今後、いろいろと他の分野のアプリケーションにも応用できる機能だと思っています。
 当然ながら、MAGICはV10で開発しています。V10になって、「こんなこともできるようになったんだ!」というようなものが幾つもありまして、それらもあわせてこのページで順次紹介できれば良いなと思っております。




メールアーカイブって?


 そもそも、メールアーカイブって何でしょうか?
 そうですね、簡単に言えば「送受信したメールデータを全てアーカイブ(書庫化)するシステム」のことです。
 送受信したメールというのは、SMTPサーバ(MTA)を経由した送信/受信メールの全てが該当データになります。
 また、書庫化するということは、インデックスが付けられて即座に検索することが可能になるということです。
 このジャンルの製品は最近沢山出ているようです。
 注目されることになったのが、システムセキュリティや内部統制の観点からです。個人情報漏洩の抑止効果も期待されていますし、当然ながらメールデータの保存・検索に便利ですので、単に問題発生時の検索用途だけでなく、データ共有的な機能をつけている製品もあるようです。
 というわけで、メールアーカイブも百花繚乱。製品価格もピンからキリまでいろんな製品があります。
 さて、そんな中で、当メールアーカイブは果たして生きていけるのか・・・。(それは私も良く分かりません。(^^;)
 まぁ、こんなものを作ったんだけど・・・というノリで、順を追って、概要や特長について説明していきたいと思います。



当メールアーカイブの概要



 それでは、順に当メールアーカイブの概要について説明していきましょう。

メールの受信機能

 まず、メールの受信部分についてです。
 この部分は、MTAから転送されたメールを受信して、保存するためのサービスプログラムをバックグランドで実行させることで実現しています。
 この部分を担当しているのが、「KSearch.exe」というモジュールです。その設定画面を示します。

ma_001.jpg

KSARSサービスは任意のポートからメールを受信して格納することが可能です。


 タイムアウト時間や、保存単位、受信フォルダ、サービス監視ポートなどを設定することが可能です。

 ところでレシーバにメールを転送するにはどうすれば良いでしょうか?
 MTA(SMTPサーバ)は、転送機能のあるものであれば、Windows系に限らず任意のものを利用することが可能です。
 下記は、とあるメールサーバ(現e-postメールサーバの前身版)での複写転送と配送先を設定している例です。


ma_002.jpg

ダミーのアドレスを転送先アドレスとして設定します。


ma_003.jpg

ダミーで設定したドメインに対して、配送先サーバとポート番号を指定します。

ただ、上記のような場合は、受信したメールのエンベロープ情報には送信先として常にダミーのメールアドレスが入ってしまいます。(ヘッダ情報の「To」等は問題ありません。)
 これを解決するためには、「Q-Send Server」というe-post製品を使用することで解決が可能です。(いつかの機会にご紹介したいと思います。)

メール書庫化機能

 次に、メールの書庫化についてです。
 メールデータは、1件=1ファイルのXMLデータに変換されます。変換されたXMLデータは、DB2の表に格納されます。
 下の画面は、Mail2XMLという変換プログラムを起動したものです。ダイアログにより対話的に処理を指示することも可能です。が、本システムでは変換とインポート処理をバックグランドで自動的に実行しますので、この画面が表示されることはありません。


ma_004.jpg

メールのXML変換プログラムは単独で起動も可能です。


 メールデータをXMLデータにする利点には、次のようなものがあります。

  • XMLデータベースに格納して、利用が可能になる。
  • メールのエンコードの文字種別(JIS, EUC,UTF-8)によらず、全て同じコード(UTF-8)でアクセスできるようになる。
  • エンベロープ、ヘッダ、ボディ(本文)、添付ファイル(テキスト情報)などの個別の情報について、1つのXMLスキーマを利用してアクセスが可能になる。
  • XMLデータであることによる他の副次的な利点を享受できる(システム連携のデータとして利用する等)

 一番大きいのは、なんと言っても「XMLデータベースに格納できる」ということです。他の利点は付随的なものとすらいえます。
 統一的に文字コードはUTF-8に変換されますが、中にはどうしても文字化け等の理由によりXMLデータでは扱えない文字コードが混入してしまうことがあります。(DB2は結構チェックが厳しいので、そういうデータはインポート時にはじかれてしまいます。)

 下図は、DB2の管理ツールコントロールセンターで表を開いたところです。「MAIL_DATA」というカラムがXMLタイプのカラムです。これ以外にも、いくつかの属性を同じ表のカラムに格納することができるわけです。DB2 9がハイブリッドデータベースと言われる由縁ですね。


ma_005.jpg

XMLタイプのカラムがテーブルに作成されていることが分かります。



 XMLカラムの右脇のボタンをクリックすると、文書ビューアーという画面で、XMLデータの内容を確認することが可能です。
 このXMLカラムを使用することにより、送信者、宛先、件名、本文、その他任意のヘッダ項目を使用したメールの検索を行うことが可能になります。


ma_006.jpg

XMLスキーマの階層構造に従って、メールメッセージデータが格納されます。


 
 メールメッセージデータとしての殆どの属性は、このXMLという形式のカラムにテキストデータとして格納されることになるわけです。
 XMLカラムがあれば、他の従来形式のカラムは必要ないのでは?という疑問が湧き起こるかもしれませんが、リレーショナルデータベースとXMLデータベースの双方の良いところを使えるというのがハイブリッドDBの利点です。
 特に当システムでは、次のような考え方に基づき、このメール・データの定義を行っています。

  • メールデータはRFC822の仕様に基づき、極力原型に近い形態でXMLスキーマを定義する
  • XMLカラムにメールデータを格納し、メールデータの検索ができるようにする
  • ヘッダの情報のうちのいくつか(Message-Id、In-Reply-To等)は、他のカラムに展開し、またインデックスを作成するなどして、データ検索の高速化を実現する
  • ヘッダの情報の日付(Date)については、GSTに変換して、他のカラムに展開
  • XMLデータにない属性(メールサイズ、添付ファイルの数、その他)は他のカラムに展開

 関連するメールを検索する際に、ヘッダの「Message-Id」と「In-Reply-To」の関連を調べるわけですが、当初はXMLの検索機能を使って行っていたんです。でもすぐに、これは従来のリレーショナルデータベースの持つインデックス検索には及ばないことが分かりました。

 なお、検索用の表以外に、メールの実データを格納する表が別途あります。
 ER図的に描くと次のようになります。

ma_007.jpg

メールデータは、1対1に関連付けられた2つの表で構成されます。


 メールデータは、MAIL_EMLというテーブルのEML_BLOBというBLOB形式のカラムにバイナリデータとして格納されます。(暗号化したものが格納されます。)
 例えば、メールクライアント上から誤って削除してしまっても、このテーブルを利用して復元することが可能です。