XMLDB.jp

XMLDB開発支援
HOME  >  XMLDB開発支援  >  チュートリアル ~JAVA編(7):クエリーの基本2 -高度な式~

チュートリアル ~JAVA編(7):クエリーの基本2 -高度な式~

2004年6月3日 更新


クエリーに関するチュートリアル(2):高度なクエリー機能

このチュートリアルでは、最初のチュートリアルで学習したクエリーの基本的なパスと式を発展させます。このチュートリアルでは、結果シーケンスの順序を並べ替える方法、新しいXMLエレメントと属性を構築する方法(さらに、その中にデータベースからXMLフラグメントを埋め込む方法)を示します。また、(For、Let、Where、Returnを表す)FLWRステートメントを使って、変数に値を割り当て、シーケンスを反復処理し、条件に従って結果に含める、または結果から除外する方法についても説明します。


このチュートリアルでは、4文書からなる野球データベースをサンプルとして使用します(ここからダウンロードできます)。


ソート

通常、NeoCoreのクエリーの結果は、定義されない順序で戻されます。結果シーケンスを特定の順序で並べ替えたい場合には、以下のような「sortby」キーワードを使用します。


コード
string(/ND/player/@name) sortby(. descending)

これは4人のプレーヤーからなるシーケンスを対象とし、プレーヤーの「@name」属性を抽出して、それぞれをストリングに変換します。次に、(4つのストリング値からなる)結果シーケンスを降順でソートし、以下の結果を出力します。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
Willie Mays
Hank Aaron
Barry Bonds
Babe Ruth
</Query-Results>

複数のキーを指定してソートすることができます。各キーごとに、昇順(既定)または降順でソートできます。指定される最初のキーは、それ以降のすべてのキーよりも優先されます。シーケンス内の要素が、最初のキーに関して同じ値を持つ場合、2番目以降のキーに基づいて序列化します。


コード
/ND/player sortby(bats descending, @name)

コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
   <player name="Hank Aaron">
      <bats>R</bats>
...
   </player>
   <player name="Willie Mays">
      <bats>R</bats>
...
   </player>
   <player name="Babe Ruth">
      <bats>L</bats>
...
   </player>
   <player name="Barry Bonds">
      <bats>L</bats>
...
   </player>
</Query-Results>

この例では、プレーヤーの記録が、まず「bats」(打席)フィールドに応じてアルファベット降順でソートされます。その結果、R(右打席)がL(左打席)よりも先に来ます。次に、同じ「bats」値を持つ副次シーケンスが、「name」属性に従って昇順でソートされます。


ソート対象の各項目ごとに、各ソートキーが単一の値を評価する必要があります。 (たとえば明示的な変換関数の結果として)ソートキー値の型が明らかでない場合には、項目のストリング値が使用されます。それぞれのキー位置ごとに、キーの型がすべて適合しなければなりません。たとえば、最初のキーがすべて整数で、2番目のキーがすべてストリングである場合、どちらのキーも、整数とストリングの混合であってはなりません。


式の結果をソートキーにすることもできます。これによって、多様な組み合わせが可能になります。以下のクエリーは、プレーヤーの現役通算ホームラン数によってソートした後、それぞれの名前のストリング値をシーケンスとして抽出します。


コード
string((/ND/player sortby(sum(integer(batting/homers)) descending))/@name)

結果は次のようになります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
Hank Aaron
Babe Ruth
Willie Mays
Barry Bonds
</Query-Results>

以下のクエリーは、打者としてプレーした年数に応じて降順でプレーヤーをソートした後、ピッチャーとしてプレーした年数でソートし、さらに名前でソートします。


コード
string((/ND/player sortby(count(batting) descending, count(pitching) descending, @name ascending))/@name)

クエリーの結果は次のようになります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
Hank Aaron
Willie Mays
Babe Ruth
Barry Bonds
</Query-Results>

以下のクエリーは、プレーヤーが50本を超えるホームランを打った年を、ホームラン数に応じて(降順で)ソートした後、年でソートします。


コード
/ND/player/batting[homers > 50] sortby(integer(homers) descending, @year)

結果は以下のリストのようになります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
   <batting year="2001" team="SF" league="NL">
...
      <homers>73</homers>
...
   </batting>
   <batting year="1927" team="NY" league="AL">
...
      <homers>60</homers>
...
   </batting>
... etc.
</Query-Results>

XMLコンストラクタ


データベースに対してクエリーを実行するとき、データのフォーマットを出力用に再構築したいことがよくあります。これを行うには、XMLコンストラクタを使用します。 XMLコンストラクタは、単純なXML文書のように見える式です(エレメントと属性だけが存在し、名前空間とXML宣言は使用できません)。さらに、これらのエレメントまたは属性の中に、中括弧{}で囲んだ式を含めることができます。結果の出力が作成されるとき、このような式は評価されて、XMLフラグメントに埋め込まれます。以下の一連のコンストラクタは、どれも同じ結果を作成します。


コード
<message>Hello, world</message>
<message>{"Hello, world"}</message>
<message>Hello, {"world"}</message>
<message>{("world", "Hello, ") sortby(.)}</message>

出力は、以下のようになります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
   <message>Hello, world</message>
</Query-Results>

また、XMLコンストラクタに属性を含めることもできます。


コード
<career-total homers={sum(/ND/player[@name="Babe Ruth"]/batting/homers)}/>

子を持たないこのコンストラクタによって、以下が戻されます。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <career-total homers="714"/>
</Query-Results>

XMLコンストラクタをFLWRステートメントおよびソートと併用すると、とくに便利です。 これらの機能を合わせて利用すれば、XMLフラグメントからなるシーケンス全体を構築し、計算された複数の値をそれぞれに埋め込んで、 (オプションで)出力をソートすることができます。 この組み合わせによって、ほとんどどんな操作も可能です!


FLWRステートメント(For、Let、Where、Return)


FLWRステートメントを使用すれば、以下が可能です。


● シーケンスを反復処理して、変数に個々の値を割り当てる(for)

● 式の結果を変数に割り当てる(let)

● シーケンス内のいくつかの値を選んで、それらを無視する(where)

さらに

● これらの変数を使って、式の出力を計算する(return)


1つのFLWRステートメントは、1つまたは複数の「for」または「let」文節、オプションの「where」文節、および1つの「return」文節からなります。 たとえば、次のようになります。


コード
for $x in /ND/player[@name="Babe Ruth"]/batting[@year >= 1933]/homers
return <h>{integer($x)}</h>

これによって、選択されたそれぞれのホームラン数の値ごとに、1つのXMLエレメント「h」が戻されます。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <h>34</h>
        <h>22</h>
        <h>6</h>
</Query-Results>

「for」文節は、以下のように、「for」というワード、1つの変数名、「in」というワード、および1つの式からなります。


コード
for $pl in /ND/player

「let」文節は、以下のように、「let」というワード、1つの変数名、「:=」という記号、および1つの式からなります。


コード
let $x := sum(/ND/player/batting/homers)

「for」文節は、式によって作成されるシーケンス内のそれぞれの値を順番に(一定の順序で)変数に割り当て、そのような値ごとに、FLWR式の残りの部分を一度ずつ評価します。 これに対して、「let」は、式によって作成されるシーケンス全体を変数に割り当て、FLWRの残りの部分を一度だけ評価します。


FLWRステートメントで使用される変数は、ドル記号で始まる名前です(XMLエレメント名と同様に、文字、数字、ダッシュを名前に含めることができます)。 変数名の有効範囲は、FLWRステートメントの残りの部分、つまり「return」式の終わりまでです。 言い換えると、1つの「for」または「let」文節で割り当てられた変数を、後続の「for」または「let」文節や、「where」または「return」文節の中で使用することができます。 同じFLWRステートメント内で同一の変数名が2度以上出現した場合には、ステートメントの残りの部分において、より内側の割り当てがより外側の割り当てに置き換わります。


「where」文節は述部のように機能し、先行する「for」および「let」文節によって割り当てられた変数に適用されます。 「where」内の式が真と評価される場合、「return」が実行されます。そうでなければ、それらの変数値に関しては「return」がスキップされます。 たとえば、以下の例は、50ホームランを超える「batting」エレメントを集めた1つのエレメントを構築し、構築された「year」エレメントのストリング値に従ってソートします。


コード
for $y in /ND/player/batting
where $y/homers > 50
return <year>{string($y/@year)}</year>
sortby(.)

この例の重要なポイントは、「sortby」演算子がFLWR式よりも緩やかにバインドしていることです。そのため、最終的な「sortby(.)」は「for」の結果シーケンス全体に適用されます。 結果はこうなります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <year>1920</year>
        <year>1921</year>
        <year>1927</year>
        <year>1928</year>
        <year>1955</year>
        <year>1965</year>
        <year>2001</year>
</Query-Results>

この場合、「where」文節は以下の述部と厳密に同じ働きをします。


コード
for $y in /ND/player/batting[homers > 50]
return <year>{string($y/@year)}</year>
sortby(.)

ただし、述部で実行できない機能を「where」で実行できるケースがあります。 最も一般的なケースは、以下のように2つの文書の値を比較する機能であり、これはSQLの結合操作と同等です。


コード
for $p1 in /ND/player
for $p2 in /ND/player
where sum($p1/batting/homers) > sum($p2/batting/homers)
return <hey>{string($p1/@name)} hit more homers than {string($p2/@name)}!</hey>

このクエリーによって、以下が戻されます。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <hey>Willie Mays hit more homers than Barry Bonds!</hey>
        <hey>Babe Ruth hit more homers than Barry Bonds!</hey>
        <hey>Babe Ruth hit more homers than Willie Mays!</hey>
        <hey>Hank Aaron hit more homers than Barry Bonds!</hey>
        <hey>Hank Aaron hit more homers than Willie Mays!</hey>
        <hey>Hank Aaron hit more homers than Babe Ruth!</hey>
</Query-Results>

FLWRステートメントをネストさせることもできますが、ほとんどの場合、同じステートメント内で「for」文節と「let」文節を組み合わせ、各文節の入力に対して中間的にソートを実行すれば、同じ結果を達成できます。 最後の例として、以下のクエリーは一連のプレーヤーを反復処理し、各プレーヤーの打撃成績が最も良かった年を抽出して、それらの年の成績を表す数値を含むエレメントを作成します。(なお、「two-bagger」は二塁打、「dinger」は一発(ホームラン)、「extra-base」は長打です。)


コード

for $hitter in /ND/player sortby (sum(batting/homers) descending)
for $season in $hitter/batting[hits >= 200 or homers >= 50] sortby(@year)
let $extra-bases-on-long-hits := $season/doubles
                                    + 2*$season/triples
                                    + 3*$season/homers
where $extra-bases-on-long-hits > 100
return <good-year name={string($hitter/@name)} year={string($season/@year)}>
           <hits>{integer($season/hits)}</hits>
           <two-baggers>{integer($season/doubles)}</two-baggers>
           <three-baggers>{integer($season/triples)}</three-baggers>
           <dingers>{integer($season/homers)}</dingers>
           <extra-bases>{$extra-bases-on-long-hits}</extra-bases>
           {$season/stolen-bases}
       </good-year>
sortby (extra-bases descending, hits descending, @name)

このプログラム(もはや簡単なクエリーとは呼べません!)の結果は、次のようになります。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <good-year name="Barry Bonds" year="2001">
                <hits>156</hits>
                <two-baggers>32</two-baggers>
                <three-baggers>2</three-baggers>
                <dingers>73</dingers>
                <extra-bases>255</extra-bases>
                <stolen-bases>13</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1921">
                <hits>204</hits>
                <two-baggers>44</two-baggers>
                <three-baggers>16</three-baggers>
                <dingers>59</dingers>
                <extra-bases>253</extra-bases>
                <stolen-bases>17</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1927">
                <hits>192</hits>
                <two-baggers>29</two-baggers>
                <three-baggers>8</three-baggers>
                <dingers>60</dingers>
                <extra-bases>225</extra-bases>
                <stolen-bases>7</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1920">
                <hits>172</hits>
                <two-baggers>36</two-baggers>
                <three-baggers>9</three-baggers>
                <dingers>54</dingers>
                <extra-bases>216</extra-bases>
                <stolen-bases>14</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1928">
                <hits>173</hits>
                <two-baggers>29</two-baggers>
                <three-baggers>8</three-baggers>
                <dingers>54</dingers>
                <extra-bases>207</extra-bases>
                <stolen-bases>4</stolen-bases>
        </good-year>
        <good-year name="Willie Mays" year="1955">
                <hits>185</hits>
                <two-baggers>18</two-baggers>
                <three-baggers>13</three-baggers>
                <dingers>51</dingers>
                <extra-bases>197</extra-bases>
                <stolen-bases>24</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1923">
                <hits>205</hits>
                <two-baggers>45</two-baggers>
                <three-baggers>13</three-baggers>
                <dingers>41</dingers>
                <extra-bases>194</extra-bases>
                <stolen-bases>17</stolen-bases>
        </good-year>
        <good-year name="Babe Ruth" year="1924">
                <hits>200</hits>
                <two-baggers>39</two-baggers>
                <three-baggers>7</three-baggers>
                <dingers>46</dingers>
                <extra-bases>191</extra-bases>
                <stolen-bases>9</stolen-bases>
        </good-year>
        <good-year name="Willie Mays" year="1965">
                <hits>177</hits>
                <two-baggers>21</two-baggers>
                <three-baggers>3</three-baggers>
                <dingers>52</dingers>
                <extra-bases>183</extra-bases>
                <stolen-bases>9</stolen-bases>
        </good-year>
        <good-year name="Hank Aaron" year="1959">
                <hits>223</hits>
                <two-baggers>46</two-baggers>
                <three-baggers>7</three-baggers>
                <dingers>39</dingers>
                <extra-bases>177</extra-bases>
                <stolen-bases>8</stolen-bases>
        </good-year>
        <good-year name="Hank Aaron" year="1963">
                <hits>201</hits>
                <two-baggers>29</two-baggers>
                <three-baggers>4</three-baggers>
                <dingers>44</dingers>
                <extra-bases>169</extra-bases>
                <stolen-bases>31</stolen-bases>
        </good-year>
        <good-year name="Willie Mays" year="1958">
                <hits>208</hits>
                <two-baggers>33</two-baggers>
                <three-baggers>11</three-baggers>
                <dingers>29</dingers>
                <extra-bases>142</extra-bases>
                <stolen-bases>31</stolen-bases>
        </good-year>
        <good-year name="Hank Aaron" year="1956">
                <hits>200</hits>
                <two-baggers>34</two-baggers>
                <three-baggers>14</three-baggers>
                <dingers>26</dingers>
                <extra-bases>140</extra-bases>
                <stolen-bases>2</stolen-bases>
        </good-year>
</Query-Results>

この例には、注目すべき機能がたくさんあります。


● 構築されるタグは、元のデータと無関係であっても差し支えありません。 ここでは、「dingers」(一発)などのタグを独自に作成しました。

● 同じように、必要に応じて変数名を作成することができます。元の文書から派生させる必要はありません。

● 「let」を使用して、複雑な式を変数に割り当てることができます。こうすれば、クエリーの構造が明確になり、さまざまなコンテキストで同じ値を再使用できるようになります

● 「sortby」文節は、ソート対象のシーケンス内の項目から見た相対パスを使用します。 つまり、最後のソートにおける「stolen-bases」(盗塁)は、FLWRステートメントによって構築された各XML項目内の同じ名前のエレメントを参照します。

● (データベースの)XMLフラグメントへの参照をXMLコンストラクタに挿入すると、そのタグ構造も導入されます(「stolen-bases」を見てください)

● 構築されたXMLタグは元の内容の型を保持します(型が存在する場合)。したがって、「integer(extra-bases)」と指定する代わりに、「extra-bases」フィールドだけでソートを実行できます。 一方、データベースのXMLフラグメントの型はストリングと想定されるため、「stolen-bases」エレメントを数値として扱いたい場合には、明示的に変換する必要があります。


最後のヒントとして、すべての式が値のシーケンスを戻し、それを他の式で使用できることに注目してください。 つまり、上記の例に基づいて、次のようなクエリーを作成できます。


コード
let $good-years := {...上記のクエリー...}
return <total-hits>{sum($good-years/hits)}</total-hits>

これによって、このクエリー全体が次のように凝縮されます。


コード
<?xml version="1.0" encoding="UTF-8" ?>
<Query-Results>
        <total-hits>2496</total-hits>
</Query-Results>

このパワフルかつ柔軟な機能をぜひ最大限に利用して、あなたのクエリープログラミングの生産性を大いに高めてください!


▲このページのTOPへ

  • 無償で使える!XMLDB「NeoCore」
  • サイバーテック求人情報
  • メールマガジン申し込み
  • TEchScore

  • ▲NeoCoreについて記載されています!

  • ▲XMLマスター教則本です。試験対策はこれでばっちり!
Copyright (c) CyberTech corporation ltd. All ights Reserved. | サイバーテックについて | ご利用ガイド