最新のXML仕様を実践で覚える「XQueryチュートリアル」(4)
XQueryによるXML文書の結合〜2

結合を使ってメンバーごとのプロジェクトリストを作成する

 第1回「XQueryを実体験してみる」では、projects.xmlから、メンバーごとのプロジェクトリストを作成した。今回はprojects3.xmlとemployees.xmlから、メンバーごとのプロジェクトリストを作成する。example-3-2-1の問い合わせがそれである。

for $e in document("Tutorial/data/employees.xml")//employee
return
<memberlist> {
<member> {$e/@empno, $e/name , <currentDept>{$e/postings/dept[last()]/text()}</currentDept>} </member> ,
<projects> {
for $p in document("Tutorial/data/projects3.xml")//project[members/member/empno
= $e/@empno]
return
<project> {
<pname> { $p/name/text(), $p/@start } </pname> ,
<deptOfThePeriod>{$e/postings/dept[@start<=$p/@start and @end >=$p/@start]}</deptOfThePeriod>
} </project>
}</projects>
} </memberlist>

example-3-2-1] メンバーごとのプロジェクトリストを作成する


画面6 example-3-2-1の結果

 画面6を見れば分かるように、現在の所属部署<currentDept>がメンバー<member>の下に得られているのとともに、プロジェクト<projects>の下には、プロジェクトスタート時点での所属部署<deptOfThePeriod>が得られる。

 example-3-2-1はまた、第3回「XQueryの関数を使う、定義する」で紹介したユーザー定義関数を使って簡素化することもできる。example-3-2-2はユーザー定義関数を使って再定義したものである。こうすることにより、問い合わせ文が見やすくなるだけでなく、メンテナンスを行ううえでも便利である。また、current_dept()やdept_of_the_period()を別の問い合わせ文で再利用することも可能だ。

define function current_dept(xs:string $en) returns element
{
let $rtnval := document("Tutorial/data/employees.xml")//employee[@empno
=$en]/postings/dept[last()]/text()
return ( <currentDept>{$rtnval}</currentDept> )
}
define function dept_of_the_period(xs:string $e,xs:string $p) returns element
{
let $rtnval := $e/postings/dept[@start<=$p/@start and @end >=$p/@start]
return ( <deptOfThePeriod>{$rtnval}</deptOfThePeriod> )
}
for $e in document("Tutorial/data/employees.xml")//employee
return
<memberlist> {
<member> {$e/@empno, $e/name ,current_dept($e/@empno) }</member>,
<projects> {
for $p in document("Tutorial/data/projects3.xml")//project[members/member/empno = $e/@empno]
return
<project> {
<pname> { $p/name/text(), $p/@start } </pname> , dept_of_the_period($e,$p)
} </project>
}</projects>
} </memberlist>

example-3-2-2] 前述のexample-3-2-1をユーザー定義関数で簡素化したもの

量化表現式(Quantified Expression)を使う:その1

 XQueryでは量化表現式(Quantified Expression)をサポートする。次の問い合わせexample-4-1-1はexample-2-3-1を量化表現式で書き換えたものである。

for $mn in distinct-values(document("Tutorial/data/projects.xml")//member/name)
return
<memberlist>
{ <member>{$mn/text()}</member> } <projects>
{
for $p in document("Tutorial/data/projects.xml")//project
where some $m in $p//member/name satisfies $m=$mn
return <projectname>{$p/name/text()}</projectname>
}</projects>
</memberlist>

example-4-1-1] projects.xmlのメンバーごとにどういうプロジェクトに参加しているかを表すメンバーリストを作成するexample-2-3-1.xqueryを、量化表現式で書き換えた

 8行目のsome X in Y satisfies Zが量的表現式と呼ばれるもので、Yの中のXにZが満足するものが1つでもあればtrue(真)となり、そうでない場合はfalse(偽)となる。

画面7 example-4-1-1の結果

 example-4-1-1では、プロジェクトメンバーのリスト($p//member/name)の中に該当のメンバーがいるか量化表現式で選択をし、メンバーのいるプロジェクトのみがreturn句で表される結果を作成した。example-4-1-1の結果は画面7となる。

量化表現式(Quantified Expression)を使う:その2

 先ほどの量化表現式(some X in Y satisfies Z)は、1つ満足するものがあればtrue(真)の値を返すのに対して、すべて満足されたときにのみtrue(真)としたい場合は、もう1つの量化表現式(every X in Y satisfies Z)を使うことができる。つまり、Yの中のXで、Zをすべて満足するものだけを選択するものである。

 例えば、すべてのプロジェクトに参加している人を探す場合には大変便利である。example-4-2-1は、projects3.xmlとemployees.xmlの中から、すべてのプロジェクトに参加している、従業員を探し出す問い合わせである。

for $e in document("Tutorial/data/employees.xml")//employee,
$p in document("Tutorial/data/projects3.xml")//project
let $eno := $e/@empno
where every $m in $p//member/empno satisfies $m=$eno
return <result> {
<employee>{ $e/@empno, $e/name }</employee>
} </result>
example-4-2-1] すべてのプロジェクトに参加しているメンバーを探すために、量化表現式を使う

画面8 example-4-2-1の結果

 example-4-2-1はまた、example4-2-2のように書くこともできる。違いは明示的にFLWR表現式をネストして書く(example4-2-2)のかそれとも、1つのFLWR表現式で書く(example-4-2-1)かの差である。どちらの方がいいかは実装により変わってくるので、今後XQueryのオプティマイズに関する議論の1つになるだろう。

for $e in document("Tutorial/data/employees.xml")//employee
let $eno := $e/@empno
return
for $p in document("Tutorial/data/projects3.xml")//project
where every $m in $p//member/empno satisfies $m=$eno
return
<result> {
<employee>{ $e/@empno, $e/name }</employee>
} </result>
example-4-2-2] FLWR表現式をネストして書くことで、前述のexample-4-1-1と同じことを表現する

 some とevery両方を利用して、問い合わせを作ることも可能である。そこで、employees.xmlにどのプロジェクトにも参加していない従業員、岡安琢也のエントリを画面9のように追加し、employees2.xmlとする。

画面9 employees2.xml

 projects3.xmlは変更していないので、岡安琢也はどのプロジェクトにも参加していないことになる。この、projects3.xmlとemployees2.xmlに対して、どのプロジェクトにも参加していない従業員を得る問い合わせ文を、someおよびeveryの両方を使って作成してみる。内容としてはすべてのprojectに対して、まったくエントリがないもの(not(some …))を探し出せばよい。問い合わせはexample-4-2-3のように記述する。

for $e in document("Tutorial/data/employees2.xml")//employee
let $eno := $e/@empno
where every $p in document("Tutorial/data/projects3.xml")//project
satisfies not(some $m in $p//member/empno satisfies $m=$eno)
return
<result> {
<employee>{ $e/@empno, $e/name }</employee>
} </result>
example-4-2-3] どのプロジェクトにも参加していないメンバーを探す

まとめ

 ご存じのとおり、XQueryは現時点(2003年3月)ではまだワーキングドラフトである。RDBMSを語るのにSQLなしで語ることができないのと同様に、XMLデータベース(最近はNXD:Native XML Databaseといういい方をされている)を語るにはXQueryなしで語ることができなくなる。

 RDBMSが現在のように広まってきた過程において、SQLの標準化が大きな役割を果たしてきた。これは、アプリケーションプログラマが共通のデータ操作言語を覚えることは、データベース固有の操作言語をそれぞれ覚えるよりもはるかに効率的であり、SQLが広く支持されてきた結果である。同様にXQueryさえ覚えていればどのNXDでも扱うことができ、同じ結果を得ることができる、という意味では、XQueryはSQLと同じ重要さを持つ。

 今回の連載の中で、しばしばXQueryとSQLを比較して記述をしてきた。XMLのデータモデルはリレーショナルのデータモデルとは違うので、そのまま比較することにあまり意味はないかもしれない。しかし、XQueryを学習するうえで、広くエンジニアに受け入れられているSQLと比較することで、XQueryをより分かりやすく説明できるはずだ。今回のチュートリアルを通じて少しでも多くの人が、早くXQueryを使いこなせるようになることを期待する。

参考:これまでに紹介した問い合わせファイルのリスト

Query名 説明
InstallTest.xquery インストールテスト
example-2-1-1.xquery projects.xmlの中からすべてのプロジェクト名を取り出す
example-2-1-2.xquery プロジェクトの終了が遅い順番に並べ替える
example-2-1-3.xquery プロジェクトの終了が遅い順番に並べ替え、プロジェクト名のみ表示する
example-2-1-4.xquery プロジェクト名でソートし、プロジェクト名のみ表示する
example-2-2-1.xquery 2名以上メンバーが存在するプロジェクトを表示する
example-2-2-2.xquery 2名以上メンバーが存在するプロジェクトのプロジェクト名とメンバーの一覧を作る
example-2-2-3.xquery 2名以上メンバーが存在するプロジェクトのプロジェクト名とメンバーの一覧を作る。ただし、プロジェクト名は属性として作成する
example-2-2-4.xquery FLWR表現式のwhere句を使い、2名以上メンバーが存在するプロジェクトのプロジェクト名とメンバーの一覧を作る
example-2-2-5.xquery FLWR表現をネストし、2名以上メンバーが存在するプロジェクトのプロジェクト名とメンバーの一覧を作る
example-2-2-6.xquery FLWR表現式をネストし、2名以上メンバーが存在するプロジェクトのプロジェクト名とメンバーの一覧を作り、プロジェクト名でソートする
example-2-3-1.xquery projects.xmlのメンバーごとにどういうプロジェクトに参加しているかを表すメンバーリストを作成する
example-2-3-2.xquery メンバーリストにそれぞれのプロジェクト毎の工数計画を表示させる(フィルタを使う)
example-2-3-3.xquery メンバーリストにそれぞれのプロジェクトごとの計工数計画を表示させる(where句を使う)
example-2-3-4.xquery プロジェクトごとに工数計画の入ったメンバーリストのwhere句を取り除き、Tuple streamを確認する。これによりfor句がどのような働きをするか確認する
example-2-3-5.xquery プロジェクト計画の入ったメンバーリストのlet句のフィルタを取り除いて、let句がどのような働きをしているかを確かめてみる
example-2-4-1.xquery プロジェクトごとの工数をsum()関数で計算する
example-2-4-2.xquery sum()、max()、min()、avr()、count()関数を使い、プロジェクトの概要として最大工数、最小工数などを表示するための問い合わせ
example-2-4-3.xquery ユーザー定義関数を使って、プロジェクトごとの工数を合計する
example-2-4-4.xquery ユーザー定義関数を使い、メンバーごとに所属しているプロジェクトと工数の合計を表示する
example-2-5-1.xquery If-then-elseを使い、単位が複数あっても計算が正しくできるようにする(属性での対応)
projectx2.xml manpower要素の単位を、属性でなく、manpowerbyday、manpowerbyhourなど要素の違いで表現
example-2-5-2.xquery If-then-elseを使い、単位が複数あっても計算が正しくできるようにする(要素の選択で対応)
employees.xml 従業員マスター
projects3.xml プロジェクト情報をあらためてまとめた文書
example-3-1-1 projects3.xmlとemployees.xmlを結合するためのFLWR表現式
example-3-1-2 中田聡が参加するプロジェクトの一覧を作成する
example-3-1-3 現在、技術本部に所属している従業員が過去に参加したプロジェクトを検索する
example-3-1-4 技術本部に所属する従業員が過去に参加していたプロジェクトと、当時所属していた部署名を同時に表示する
example-3-1-5 プロジェクト発足時にインターネットソリューション部の従業員が参加していたプロジェクトの一覧を表す

example-3-1-6
projects3.xmlとemployees.xmlを完全な形で結合する
example-3-2-1 メンバーごとのプロジェクトリストを作成する

example-3-2-2
example-3-2-1をユーザー定義関数で簡素化したもの

example-4-1-1
projects.xmlのメンバーごとにどういうプロジェクトに参加しているかを表すメンバーリストを作成するexample-2-3-1.xqueryを、量化表現式で書き換えた
example-4-2-1 すべてのプロジェクトに参加しているメンバーを探すために、量化表現式を使う
example-4-2-2 FLWR表現式をネストして書くことで、example-4-1-1と同じことを表現する
example-4-2-3 どのプロジェクトにも参加していないメンバーを探す

5/5

Index
連載:XQueryチュートリアル
  XQueryを実体験してみる
  XQueryのFLWR表現式を使いこなす
  XQueryの関数を使う、定義する
  XQueryによるXML文書の結合〜1
XQueryによるXML文書の結合〜2


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間