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

XMLの本格利用に向けた重要な技術の1つがXMLデータベースの発展だ。そのカギを握るのは、問い合わせ言語XQueryの標準化である。現在、XQueryは標準化目前のところまできており、実際にXQueryの実装も登場している。本記事は、そのXQueryの実践を目的とした。

戌亥稔
ビーコンIT
2003/4/1


 XQueryの特徴の1つとしてあげられるのは、SQLでおなじみの結合(Join)をFLWR表現式を使って実行できる点である。今回はXQueryチュートリアルの最終回として、その文書の結合について解説する。

本記事は、ソフトウェアAGおよびビーコンITが無償で公開しているXQueryプロセッサのQuiPを利用して、XQueryの機能を実践解説している。QuiPの入手方法などについては、第1回「XQueryを実体験してみる」を参照のこと。
また、 W3Cが発表したXQueryのワーキングドラフト最新版では、XQueryの表現式として従来のFLWR表現式がFLWOR表現式(同様にフラワーと読む)に変更された。これはFLWR表現式のFor、Let、Where、Returnに、Order byが加えられたものである。しかし、この記事で使用しているQuiP v2.2.1.1は2002年4月30日版のワーキングドラフトに対応しているため、本記事ではFLWRという表記のまま説明を続ける。

FLWR表現式で結合(Join)を実行する

 まず、XML文書の結合を行うためのサンプルを用意しよう。新しいXML文書としてemployees.xmlをリスト1のように定義する。employees.xmlはいわゆる社員マスターである。社員マスターは、従業員コード<empno>をキーとして構成されており、それに対して、名前<name>、役職<titles>、所属部署<postings>などの要素がある。また、XMLの特徴を表すために、役職<titles>、所属部署<postings>それぞれの子要素としてさらに役職<title>と部署<dept>を定義し、それを繰り返すことで過去の役職や所属部署の経歴まで表現できるようにした。

<?xml version="1.0" encoding="UTF-8" ?>
<employees>
  <employee empno="121">
    <name>川泉陽一</name>
    <titles>
      <title start="2001/04/01" end="2002/09/30">次長</title>
    </titles>
    <postings>
      <dept start="2001/04/01" end="2002/01/31">エンタープライズソリューション部</dept>
      <dept start="2002/02/01" end="2002/09/30">インターネットソリューション部</dept>
    </postings>
  </employee>

  <employee empno="122">
    <name>中田聡</name>
    <titles>
      <title start="2001/04/01" end="2002/09/30">主任</title>
    </titles>
    <postings>
      <dept start="2001/04/01" end="2002/03/31">インターネットソリューション部</dept>
      <dept start="2002/04/01" end="2002/09/30">技術本部</dept>
    </postings>
  </employee>

  <employee empno="123">
    <name>本岡欣也</name>
    <titles>
      <title start="2001/04/01" end="2002/09/30">主任</title>
    </titles>
    <postings>
      <dept start="2001/04/01" end="2003/03/31">技術本部</dept>
    </postings>
  </employee>
</employees>
リスト1 従業員マスターとなるemployees.xml

 このemployees.xmlを、これまで紹介してきたプロジェクト一覧などを示すprojects.xmlやprojects2.xmlと結合する。そうすると、各人の属するプロジェクトと経歴が一度に表現される、というわけだ。

 XML文書を結合する前にprojects.xmlを変更しprojects3.xmlとする。変更点は、個人を特定するキーとして<empno>を定義し、これまで使ってきた<name>はemployees.xmlの中で定義されているので、冗長性をなくすために省く。つまり、メンバーの情報はすべて従業員マスターであるemployees.xmlに集まり、projects3.xmlにはプロジェクト情報だけが収まっていることになる。

<?xml version="1.0" encoding="UTF-8" ?>
<projects>
  <project code="200200020" start="2002/01/20" end="2002/03/31">
    <name>XMLによる文書管理システム</name>
     <members>
      <member>
        <empno>121</empno>
        <manpower unit="hour">100</manpower>
      </member>
      <member>
        <empno>122</empno>
        <manpower unit="hour">50</manpower>
      </member>
    </members>
  </project>
  <project code="200200025" start="2002/02/15" end="2002/03/25">
    <name>XMLによるB2Bシステム構築</name>
      <members>
        <member>
          <empno>123</empno>
          <manpower unit="hour">100</manpower>
        </member>
        <member>
          <empno>121</empno>
          <manpower unit="hour">50</manpower>
        </member>
        <member>
          <empno>122</empno>
          <manpower unit="day">5</manpower>
        </member>
      </members>
    </project>
    <project code="200200031" start="2002/03/15" end="2002/03/31">
      <name>モバイルシステムの構築</name>
      <members>
        <member>
          <empno>121</empno>
          <manpower unit="hour">10</manpower>
        </member>
      </members>
    </project>
  <project code="200200035" start="2002/04/01" end="2002/04/30">
    <name>Web Servicesプロジェクト</name>
    <members>
      <member>
        <empno>121</empno>
        <manpower unit="hour">20</manpower>
      </member>
      <member>
        <empno>122</empno>
        <manpower unit="day">5</manpower>
      </member>
    </members>
  </project>
</projects>
リスト2 プロジェクト情報をまとめたprojects3.xml

projects3.xmlとemployees.xmlを結合

 では、この2つのXML文書を結合する手順について見ていこう。下記のexample-3-1-1はprojects3.xmlとemployees.xmlを結合して、すべてのプロジェクトにおける従業員のリストを作成するためのFLWR表現式だ。

 第2回「XQueryのFLWR表現式を使いこなす」で試したように、let句ではタプル(結果の組)を作成することができる。下記のexample-3-1-1では、projects3.xmlのそれぞれの<project>に対して、別のXML文書(employees.xml)をある条件でバインドしている。その条件というのが、let句に記述されたemployees.xml内のemployee/@empnoとprojects3.xml内のproject/members/member/empnoが等しい、ということである。

for $p in document("Tutorial/data/projects3.xml")//project
let $e := document("Tutorial/data/employees.xml")//employee[@empno=
$p/members/member/empno]
return
<result>
  {
    $p/name , <members>{$e/name}</members>
  }
</result>
example-3-1-1] projects3.xmlとemployees.xmlを結合するためのFLWR表現式

 まず、project($p)に対して、let句によってバインドされたemployee($e)がタプルを生成している。以下が1回目のforループで作成されるタプルである。

{project/name="XMLによる文書管理システム", (empno="121",empno="122")}

 この結果、return句では、次のようなノードを作成する。

<name>XMLによる文書管理システム</name>
<members>
<name>川泉陽一</name>
<name>中田聡</name>
</members>

これが2回目以降のforループ(つまりすべてのプロジェクト)に対して繰り返し実行される。下記はそれぞれのループで作成されるタプルだ。

 2回目のループで作成されるタプル。

{project/name=" XMLによるB2Bシステム構築", (empno="121",empno="122", empno="123")}

 3回目のループで作成されるタプル。

{project/name=" モバイルシステムの構築", (empno="121")}

 4回目のループで作成されるタプル。

{project/name=" Web Servicesプロジェクト", (empno="121",empno="122")}

 こうしたものが組み合わされることで、example-3-1-1の結果は画面1のようになる。

画面1 projects3.xmlとemployees.xmlを結合した結果

特定の従業員が参加するプロジェクトの一覧

 次の問い合わせexample-3-1-2は、中田聡(すなわち従業員番号<empno>が122番)が参加するプロジェクトの一覧を作成する。example-3-1-2の1行目、2行目ではfor句を2回記述している。for句とlet句の違いは何度も説明してきたがここで繰り返すと、let句は直積を取らないが、for句は直積を取る、という点にある。

for $p in document("Tutorial/data/projects3.xml")//project
for $e in document("Tutorial/data/employees.xml")//employee[@empno=
$p/members/member/empno]
where $e/name="中田聡"
return
<p>
{
$p/name , $e/name
}
</p>

[example-3-1-2] 中田聡が参加するプロジェクトの一覧を作成する

 「標準化目前:注目のXML問い合わせ言語『XQuery』」でも説明したように、SQLの場合はFROM句が直積を取る。参考のため、もしもexample-3-1-2をSQLで書くとしたら、次のようになる。ここでは、projectsがプロジェクトテーブル、employeesが従業員テーブルを表すと仮定する。

SELECT projects.name, employees.name
FROM projects, employees
WHERE employees.name='中田聡' ←empno='121'をname='中田聡'に変更

 もちろんリレーショナルデータベースでは1つのテーブルで階層構造を記述できないので、厳密には比較できないが、SQLをよく知っている人がXQueryを理解するうえでは分かりやすい比較である。2つの問い合わせを比べてみると分かるが、SQLのFROM句で行っているような役割を、XQueryではfor句を使って行う。example-3-1-2の結果は画面2のようになる。

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

ある部署の従業員が過去に参加したプロジェクト

 次はもう少し、XQueryの特徴を表した結合を実行してみる。employees.xmlの中では、過去の所属部署を履歴で表すために、<postings>のタグの下に、<dept>タグを繰り返し(オカレンス)で持った。そして、常に最後のオカレンスが最新の所属部署を表すものとする。この場合最後の部署で検索すれば、現在所属している部署で検索ができる。そこでまず、現在技術本部に所属している従業員を検索する。そして該当する人物が、過去から現在まで参加したプロジェクトの一覧を出す、といった検索が可能である。この場合example-3-1-3のようなXQuery文を作成すればよい。

for $p in document("Tutorial/data/projects3.xml")//project
for $e in document("Tutorial/data/employees.xml")//employee[@empno=
$p/members/member/empno]
where $e/postings/dept[last()]="技術本部"
return
<p>
{
$p/name , $e/name , <dept>{$e/postings/dept[last()]/text()}</dept>
}
</p>

example-3-1-3] 現在、技術本部に所属している従業員が過去に参加したプロジェクトを検索する

 where句の中で記述している、

$e/postings/dept[last()]="技術本部"

はlast()が示すとおりdeptの要素の最後のオカレンスを表し、現在の所属部署が技術本部である従業員を検索することを意味している。そしてreturn句の中の

<dept>{$e/postings/dept[last()]/text()}</dept>

は、同様に最新の所属部署を結果として得ることを表している。この結果は画面3に示すとおりである。

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

 また結果を得るときに、各プロジェクト発足当時の所属部署が入っていれば便利である。もちろん、XQueryではlet句を使うことで、このような問い合わせも簡単に実行することができる。example-3-1-4がその例で、結果は画面.4となる。

for $p in document("Tutorial/data/projects3.xml")//project
for $e in document("Tutorial/data/employees.xml")//employee[@empno=
$p/members/member/empno]
let $d := $e/postings/dept[@start<= $p/@start and @end>=$p/@start]
where $e/postings/dept[last()]="技術本部"
return
<p>
{
<project start="{$p/@start}">{$p/name/text()}</project> , $e/name , $d
}
</p>
example-3-1-4] 技術本部に所属する従業員が過去に参加していたプロジェクトと、当時所属していた部署名を同時に表示する

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

プロジェクト発足時に、特定の部署から参加していた場合

 同様に、各プロジェクト発足時にインターネットソリューション部のメンバーが参加していたプロジェクトの一覧を作成する場合には、example-3-1-5のような問い合わせとなり、結果は画面5となる。

for $p in document("Tutorial/data/projects3.xml")//project
for $e in document("Tutorial/data/employees.xml")//employee[@empno=
$p/members/member/empno]
let $d := $e/postings/dept[@start<= $p/@start and @end>=$p/@start]
where $d="インターネットソリューション部"
return
<p>
{
<project start="{$p/@start}">{$p/name/text()}</project> , $e/name , $d
}
</p>
example-3-1-5] プロジェクト発足時にインターネットソリューション部の従業員が参加していたプロジェクトの一覧を表す

画面5 example-3-1-5の結果

projects3.xmlとemployees.xmlを完全な形で結合

 example-3-1-6はprojects3.xmlとemployees.xmlを完全な形で結合するためのFLWR表現式である。まず、projects3.xml中のメンバーは従業員番号しか入っていないので、それに従って、employees.xmlから名前を取り出す。また、所属部署に関しては、プロジェクトがスタートする時点での所属部署を従業員マスターの所属部署の履歴より結合して結果を得ている。

for $p in document("Tutorial/data/projects3.xml")//project
return
<project>
{ $p/@code , $p/@start , $p/@end , $p/name , <members> {
for $m in $p/members
for $e in document("Tutorial/data/employees.xml")//employee[@empno=
$m/member/empno]
return
<member>
{ $e/@empno, $e/name , $e/postings/dept[@start<=$p/@start and @end >=$p/@start],
$m/member[empno=$e/@empno]/manpower
}
</member>
}</members>
}
</project>

example-3-1-6] projects3.xmlとemployees.xmlを完全な形で結合する

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

 example-3-1-6の結果(画面6)を見ると、1番目のプロジェクト「XMLによる文書管理システム」のメンバー「川泉陽一」は所属部署が「エンタープライズソリューション部」で、2番目のプロジェクト「XMLによるB2Bシステム構築」のメンバー「川泉陽一」は所属部署が「インターネットソリューション部」になっている。これは1番目のプロジェクト開始時期が“2002/01/20”であり、2番目のプロジェクト開始時期が“2002/02/15”となっているからである。employees.xmlを見れば川泉陽一は“2002/02/01”にエンタープライズソリューション部からインターネットソリューション部に異動していることが分かる。example-3-1-6はこれを踏まえた形で結果を作成している。

 このように、XQueryでは、XMLのデータモデルの特徴を生かした形での2種類以上の文書を結合し、処理できるようになっている。

 

4/5

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


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

注目のテーマ

HTML5+UX 記事ランキング

本日月間