DB2 9 での XML クエリー・パフォーマンスを高める XML 索引を学ぶ

DB2® 9 は pureXML ストレージを提供しており、またクエリー言語として XQuery と SQL/XML を使用できます。XML 索引はクエリー・パフォーマンスを高めるために必須ですが、クエリー評価に XML 索引をどう使うかは、クエリー述部の構成によって異なります。この記事では、索引を使うことによってクエリーが期待どおり高速化されるように、一貫性を持った方法で XML クエリーを書き、XML 索引を作成するためのガイドラインを説明します。また、パフォーマンス上の問題点を検出するためには、XML クエリー実行計画の中の何を調べればよいか、またそうした問題の修正方法についても学びます。最も重要なガイドラインは、ダウンロードできる「cheat sheet (虎の巻)」の中に要約されています。

Matthias Nicola, DB2/XML Performance, IBM Silicon Valley Laboratory

Author photo: Matthias NicolaNicola 博士は、IBM Silicon Valley Lab の XML データベース・パフォーマンスの技術リーダーです。彼の業務は、XQuery、SQL/XML、また DB2 ネイティブの XML 機能を含め、DB2 での XML パフォーマンスのあらゆる側面を対象としています。Nicola 博士は DB2 XML 開発チームや XML を使用する顧客やビジネス・パートナーと緊密に業務を進めながら、XML ソリューションの設計や実装、最適化に協力しています。IBM に入社する前には、Informix Software でデータ・ウェアハウスのパフォーマンスに関する業務を行っていました。また、分散データベースや複製データベースに関する研究と業界プロジェクトにも 4 年間携わっていました。彼は、1999年にドイツの Technical University of Aachen でコンピューター・サイエンスの博士号を取得しています。



2006年 11月 02日

はじめに

DB2® 9 は pureXML ストレージの他に、XML 索引と、クエリー言語としての XQuery と SQL/XML、XML スキーマ・サポート、そして Import/Export と Runstats などのユーティリティーに対する XML エクステンションを提供しています。リレーショナル・クエリーの場合と同じように、索引は XQuery 文や SQL/XML 文のパフォーマンスを高めるために必須のものです。DB2 では、XML 列に対してパス専用の XML 索引を定義できるようになっています。これはつまり、述部や結合に頻繁に使われる、選択された要素や属性の索引付けに XML 索引を使えるということです。例えば図 1 のサンプル・データの場合、books 表の XML 列 bookinfo の中にあるすべての文書に対して、著者 (author) ID に基づいて参照や結合を行う際に

create table books(bookinfo XML);

create index idx1 on books(bookinfo) generate keys 
using xmlpattern '/book/authors/author/@id' as sql double;
図 1. テキスト型 (シリアル化) フォーマットを持ち、構文解析された (階層構造) フォーマットを持つサンプル XML 文書

DB2 は、1 つの XML 列のすべての文書に対して 1 つの XML スキーマを強制しないため、特定の要素や属性に対するデータ型を事前に推測できません。そのため、各 XML 索引に対してターゲット型を指定する必要があります。この記事の後の方では、なぜターゲット型が重要なのかを説明します。XML 索引に使用できる型は、下記のとおりです。

  • VARCHAR(n): ストリング値を持ち、その最大長が n であることが既知であるノードの場合。
  • VARCHAR HASHED: ストリング値を持ち、その長さが任意であるノードの場合。この索引は実際のストリングのハッシュ値を含んでおり、範囲述部に対してではなく、等価述部 (equality predicate) に対してのみ使われます。
  • DOUBLE: 任意の数値型を持つノードの場合。
  • DATE と TIMESTAMP: 日付値またはタイムスタンプ値を持つノードの場合。

VARCHAR(n) 索引の長さはハード制約です。索引付けされた要素または属性の値が最大長 n を越える文書を挿入すると、その挿入は失敗します。同様に、VARCHAR(n) 索引に対する create index 文は、n よりも大きな値があると失敗します。

DOUBLE や DATE、TIMESTAMP 索引などのデータ型は、 ハード制約ではありません。例えば author ID 属性に対する索引 idx1 は、(こうした ID は数値型のはずなので) DOUBLE として定義されます。もし、author ID として「MN127」(これは非数値型です) という値を持つ文書を挿入すると、文書は挿入されますが、値「MN127」は索引に追加されません。これは、DOUBLE 索引は数値述部しか評価できず、値「MN127」は数値検索条件に合致しないためです。従って、索引に値「MN127」がなくても正しいのです。

XML 索引の定義の詳細については、「Indexing XML Documents in DB2」(developerWorks、2006年5月) を参照してください。これから先の、XML 索引の使い方に関する説明では、DB2 で XML データをクエリーするための基本概念に慣れていることを前提にしています。これについて詳しくは、以前の記事のうち、入門編として「SQL を使った DB2 XML データの照会」(developerWorks、2006年3月) と「Query DB2 XML data with XQuery」(developerWorks、2006年4月) を、そしてさらに例をあげて詳しく説明した「pureXML in DB2 9: Which way to query your XML data?」(developerWorks、2006年6月) などを参照してください。


XQuery 文と SQL/XML 文に使用できる XML 索引

リレーショナル・クエリーの場合と同じく、XQuery 文や SQL/XML 文のパフォーマンスを高めるために、索引は必須です。アプリケーションが DB2 に対してリレーショナル・クエリーまたは XML クエリーを送信すると、クエリー・コンパイラーは、クエリー述部と既存の索引定義とを比較し、そのクエリーの実行に利用できる索引がないかどうかを判断します。このプロセスは「索引の照合」と呼ばれ、与えられたクエリーに使えそうな一連の索引 (空の場合もあります) が作成されます。こうした索引はコスト・ベースのオプティマイザーへの入力となり、オプティマイザーは、それらのどれかを使うべきかどうか判断します。この記事では、オプティマイザーによる索引の選択ではなく、索引の照合の方に焦点を当てます。オプティマイザーに関しては、「runstats」を実行し、データに関する正確な統計をオプティマイザー提供する以外、その判断に手を加える余地がほとんどありません。しかし、索引の照合に関しては、大いに手を加える余地があるのです。

索引の照合は、リレーショナルの場合には通常は単純です。DB2 は、1 つのリレーショナル列に対して定義された索引を使って、この列に対するすべての等価述部または範囲述部に答えます。しかし XML 列の場合は、ずっと複雑です。リレーショナル列に対する索引は、その列の値すべてを含みますが、XML 索引は、XML パターンに一致し、かつ索引定義の中のデータ型に一致するノード値しか含みません。従って、XML 索引が「正しい」データ型であり、少なくとも、XML クエリー述部を満足するすべての XML ノードを含みさえすれば、XML 索引を使って XML クエリー述部を評価することができます。つまり、XML 索引として使えるためには、次の 2 つを満足することが鍵となります。

  1. XML 索引定義による制限が、クエリー述部と同じか、あるいはクエリー述部よりも緩い (「包含関係」)。
  2. 索引のデータ型が、クエリー述部のデータ型と一致する。

この記事では、こうした要求を満足する XML 索引と XML クエリーの設計方法と、一般的な落とし穴を避けるための方法について説明します。そのために、まずクエリーの実行計画を理解する必要があります。DB2 の中の既存の説明ツール (Visual Explain や db2exfmt など) を使うと、従来の SQL を見る場合と同じように、XQuery と SQL/XML に対するクエリー実行計画を見ることができます。


XML クエリーの評価: 実行計画と新しい演算子

DB2 9 では、XML クエリーを実行するために、新たに 3 つの内部クエリー演算子 (XSCAN と XISCAN、そして XANDOR) を導入しています。DB2 では、こうした新しい演算子を既存のクエリー演算子 (TBSCAN や FETCH、そして SORT など) と組み合わせることによって、SQL/XML や XQuery に対する実行計画を生成することができます。では、これら 3 つの新しい演算子と XML 索引とが、クエリー実行計画の中でどのように動作するのかを調べてみましょう。

XSCAN (XML Document Scan)
DB2 は XSCAN 演算子を使って XML 文書ツリーをトラバースし、また必要に応じて述部を評価し、文書断片と値を抽出します。XSCAN は「XML 表スキャン」ではありませんが、各文書を処理するための表スキャンの後に、実行計画の中に現れる場合があります。

XISCAN (XML Index Scan)
XISCAN 演算子は、リレーショナル索引に対する既存のリレーショナル索引スキャン演算子 (IXSCAN) と同じく、XML 索引に対する参照、つまりスキャンを行います。XISCAN は値述部 (例えば /book[price = 29] や where $i/book/price = 29 のようなパスと値の対など) を入力し、そして一連の行 ID とノード ID を返します。行 ID は対象となる文書を含む行を特定し、ノード ID はそうした文書の中の対象ノードを特定します。

XANDOR (XML Index AND'ing)
XANDOR 演算子は、複数の XISCAN を駆動することによって、2つ以上の等価述部を同時に評価します。そして、そうした述部すべてを満足する文書群の行 ID を返します。

では、クエリーの実行計画を理解するために、索引を持たない場合、1 つの索引を持つ場合、複数の索引を持つ場合それぞれについて、クエリーのサンプルを見てみましょう。

XQUERY 
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $i/book/title = "Database systems" and $i/book/price = 29
return $i/book/authors

create index idx1 on books(bookinfo) generate keys 
using xmlpattern '/book/title' as sql varchar(50);

create index idx2 on books(bookinfo) generate keys 
using xmlpattern '/book/price' as sql double;

図 2 を見ると、このクエリー (db2exfmt を単純化した出力) に対して 3 つの異なる実行計画があることがわかります。実行計画の中の論理フローは下から上、そして左から右なので、こうした実行計画を理解するにはツリーの一番左側の一番下から読む必要があります。

一番左側の計画 (a) は、このクエリーの述部に使える索引がない場合に使われます。表スキャン演算子 (TBSCAN) は、表「BOOKS」から、すべての行を読み取ります。ネスト・ループ結合 (NLJOIN) 演算子は、それぞれの行に対して、対応する XML 文書へのポインターを XSCAN 演算子に渡します。つまり NLJOIN は 2 つの入力レグを持つ従来の結合としては動作しませんが、NLJOIN を使うと XSCAN 演算子で XML データにアクセスできるようになります。XSCAN 演算子は各文書をトラバースしてその述部を評価し、もし述部が満足されると「authors」要素を抽出します。RETURN 演算子によってクエリー実行は完了し、クエリー結果が API に返ります。

図 2. 3 つの実行計画: (a) 索引がない場合、(b) 索引が 1 つの場合、(c) 索引が 2つの場合

2 つの述部のうちの 1 つに対する索引がある場合 (例えば /book/price に対する索引 idx1 ) には、図 2 の実行計画 (b) のようになります。XISCAN は、パスと値の対 (/book/price, 29) を持つ索引を検出し、price が 29 である文書に対する行 ID を返します。こうした行 ID は、(もし重複がある場合には) 重複を削除され、その表に対して今後行われる I/O を最適化するように、ソートされます。そうすると行 ID スキャン (RIDSCN) 演算子はこうした行 ID をスキャンし、行プリフェッチをトリガーし、そして行 ID を FETCH 演算子に渡します。FETCH 演算子は、それぞれの行 ID に対応する行を表から読み取ります。この計画の利点は、表の中の行の、ごく一部のみが取得されること、つまり「price」が 29 である行のみが取得されることです。これは、すべての行を読み取る完全な表スキャンよりも、ずっとコストが低くて済みます。XSCAN 演算子は、フェッチされた各行に対応する XML 文書を処理します。XSCAN 演算子は「title」の述部を評価し、もし述部が満足されると「authors」要素を抽出します。2 番目の述部が真ではない文書は数多くあるはずなので、その真でない文書を取り除くために、XSCAN はまだまだ大量の作業を行うかもしれません。従って、2 番目の述部も索引でカバーされていれば、もっとパフォーマンスが高くなる可能性があります。

もし両方の述部に対する索引がある場合には、図 2 の実行計画 (c) のようになります。この計画は、述部と索引それぞれのために、2 つの XISCAN を使っています。XANDOR 演算子は、この 2 つの XISCAN を使って 2 つの索引を交互に調べ、両方の述部に一致する文書の行 ID を効率的に見つけます。そうすると、FETCH 演算子はこうした行のみを取得すればよくなるため、表に対する I/O が最小になります。そして XSCAN は、それぞれの文書から「authors」要素を抽出します。もし述部がパスの中に // または * を含んでいたら、あるいは範囲比較 (< や > など) を使っている場合には、XANDOR ではなく、IXAND (index AND'ing) を使います。どちらも論理的には同じジョブを実行しますが、それぞれ異なる述部のタイプに対して動作し、また最適化動作が異なります。

オプティマイザーは、たとえ索引が使えるとしても、使わずに済ますこともできます。例えば、もし 2 番目の索引を使っても表から取得する行の数を大幅に減らせない場合には (例えば、索引をアクセスするためのコストが、表 への I/O を減らしたことによるコスト節約を上回る場合など)、オプティマイザーは計画 (c) ではなく計画 (b) を選ぶこともできます。しかしオプティマイザーは、考えられる索引をすべて考慮した上で、最もコストの低い、そして最も実行時間の短い計画を選ぶ必要があります。言い換えると、XML 索引として適切であるためには、次の 2 つの要件を満足する必要があります。

  • XML 索引は、少なくとも、述部を満足する XML ノードすべてを含む。
  • クエリー述部の中のデータ型と索引定義とが相互に対応している。

XML 索引とクエリー述部でのワイルドカード

ワイルドカード、// と * は、索引とクエリー述部との間の包含関係に影響します。これは、例えばパス式 /book/price と //price とは異なるためです。/book/price というパスは、要素「book」の直接の子である price 要素すべてを特定します。一方 //price というパスは、XML 文書の中の、どのレベルの、どこにある price 要素も特定します。従って /book/price は、//price で指定される要素のサブセットを特定することになります。//price は /book/price を「含みます」が、逆に /book/price が //price を含むことはありません。

では、これが索引としての適切さにどう影響するのかを調べてみましょう。一例として、下記のクエリーを見てください。これは、表 1 の 4 種類の where 節に対応する索引の使い方を示しています。

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $i/book/price = 29
return $i/book/authors

表 1 の右側 2 列は 2 種類の索引定義を示しており、それぞれの行は、その 2 種類の索引でその述部を評価できるか (+)、あるいは評価できないか (-) を示しています。では、その索引が各述部に対して適切かどうかを調べるために、表 1 の各行を調べてみましょう。

最初の述部に対して、/book/price への索引は、「book」の直接の子である「price」要素しか含まないので、適切ではありません。この索引は、book よりも深いレベルにある「price」要素を含んでいません (price 要素は表のどこかにあるかもしれず、述部パス $i//price と一致するかもしれません)。従って、もし DB2 が /book/price という価格に対して索引を使うと、その索引は不完全な結果を返すかもしれません。2 番目の、//price への索引は、(述部として要求されるとおり) その文書のすべてのレベルのすべての price 要素を含むため、適切です。

3 番目の述部は、ワイルドカードとして星印 (*) を使っているため、「book」の下にある、29 の値を持つすべての子要素を探します。この述部を満足するのは「price」要素だけではありません。例えば、要素 /book/title の中で 29 の値を持つ文書は正当に一致しています。しかし title 要素は、表 1 の2 つの索引のどちらにも含まれていません。つまり、どちらの索引を使っても DB2 はこの述部に対して不完全な結果を返す可能性があるため、どちらの索引も使えません。

表 1. XML 索引と述部でのワイルドカードを使った索引の適格性
#述部/索引定義xmlpattern '/book/price' as sql doubleを使うxmlpattern '//price' as sql doubleを使う
1ただし $i//price = 29-+
2ただし $i/book/price = 29++
3ただし $i/book/* = 29--
4ただし $i/*/price = 29-+

4 番目の述部 $i/*/price = 29 は、単に「book」要素だけではなく、あらゆるルート要素の下にある price 要素を探します。もし /journal/price というパスを持つ文書があったとすると、その文書は述部 $i/*/price = 29 を満足するかもしれませんが、/book/price への索引には含まれません。つまりこの索引を使った場合も DB2 は不完全なクエリー結果を返す恐れがあるため、この索引は使えません。しかし //price への索引は、ルート要素に関係なく、あらゆる price 要素を含みます。

要約すると、DB2 クエリー・コンパイラーは、述部で検索するものを索引がすべて含むように、索引による制限が述部と同じか、述部よりも緩いことを確認できる必要があります。

索引定義の中でワイルドカードを使うと、そのつもりがなくても必要以上のノードに索引付けしてしまう可能性があることに注意してください。目的の要素や属性へのパスを索引定義やクエリーの中で指定する際には、ワイルドカードを使わず、できるだけ正確なパスを使うべきです。//* や //text() など、非常に汎用的な XML 索引パターンも使えますが、使う場合には十分注意する必要があります。例えば、//* に索引を付けると、リーフ要素ではない要素まで索引付けしてしまいます。これは通常使い道がなく、Varchar(n) 索引の長さ制限をすぐに超えてしまいます。


XML 索引とクエリー述部での名前空間

名前空間が含まれている場合には、XML 索引が適切かどうか、注意が必要です。まず何よりも、表の中の XML 文書が名前空間を含んでいる場合には、索引定義にそれを考慮に入れる必要があります。これもまた、索引と述部との包含関係にたどり着きます。一例として、次の XML 文書と索引定義を見てください。

<bk:book xmlns:bk="http://mybooks.org">
   <bk:title>Database Systems</bk:title>
   <bk:price>29</bk:price>
</bk:book>
CREATE INDEX idx3 ON books(bookinfo)
GENERATE KEYS USING XMLPATTERN '/book/price' AS SQL DOUBLE;

索引 idx3 は、空の名前空間を持つ /book/price 要素に対する索引として定義されているため、このサンプル文書に対する索引エントリーを含みません。しかし次の索引定義のいずれかを使えば、price 要素に適切に索引を付けることができます。

CREATE INDEX idx4 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'declare namespace bk="http://mybooks.org"; /bk:book/bk:price' AS SQL DOUBLE

CREATE INDEX idx5 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'declare default element namespace "http://mybooks.org"; /book/price' AS SQL DOUBLE

CREATE INDEX idx6 ON books(bookinfo) GENERATE KEYS USING XMLPATTERN 
'/*:book/*:price' AS SQL DOUBLE

索引 idx4 は、この文書に一致する名前空間と接頭辞を明示的に宣言しています。索引 idx5 は、この名前空間をデフォルト名前空間として宣言しています。デフォルト名前空間が暗黙指定されるため、XML パターン /book/price の中で接頭辞は使いません。索引 idx6 は単純にワイルドカードを使って、どんな名前空間とでも一致するようにしています。XQuery 文の述部を構成する場合にも、同じオプションを使うことができます。

クエリー 4:
XQUERY declare namespace bk="http://mybooks.org";
for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/bk:book
where $b/bk:price < 10
return $b
クエリー 5:
XQUERY declare default element namespace "http://mybooks.org";
for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
where $b/price < 10
return $b
クエリー 6:
XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/*:book
where $b/*:price < 10
return $b

表 2 の各行は、この 3 つのクエリーに対応し、そして各列は、上記で定義した idx3 から idx6 までの索引に対応しています。表 2 を見ると、いくつか重要なことがわかります。第 1 に、名前空間を持たない idx3 は、名前空間を考慮するクエリーには使えません。次に、クエリー 4 の行とクエリー 5 の行の中身は同じであり、また idx4 の列と idx5 の列の中身も同じです。これは、明示的な名前空間定義とデフォルト名前空間定義は論理的に同じであり、同じことを異なる方法で表現したにすぎないためです。どちらを使っても、索引の照合には影響しません。名前空間にワイルドカードを使った索引 idx6 は、どのサンプル・クエリーでも使うことができ、さらには $b/price < 10 のような、名前空間を持たない述部にも使えます。また索引 idx6 は、クエリー 6 の述部の一致比較に使える唯一の索引でもあります。索引 idx4 と idx5 は、ある 1 つの特定の名前空間に対する索引を含んでいますが、任意の名前空間にある本の price を探すクエリー 6 に使うことはできません。従って包含関係の要求に違反しています。

表 2. XML 索引と述部での名前空間を使った索引の適格性
#クエリー/索引定義idx3 (名前空間なし)idx4 (明示的名前空間)idx5 (デフォルト名前空間)idx6 (名前空間ワイルドカード)
1クエリー 4 (明示的名前空間)-+++
2クエリー 5 (デフォルト名前空間)-+++
3Query 6 (名前空間ワイルドカード)---+

XML 索引とクエリー述部でのデータ型

適切な索引であるためには、ワイルドカードや名前空間を使って索引と述部を適切に含めることの他に、述部と索引のデータ型が一致している必要があります。これまでにあげた例では、/book/price 要素は必ず DOUBLE として索引付けされていました。しかし本の価格を、表 3 のように VARCHAR として索引付けすることもできます。ただし、値述部が、リテラル値の型で決まるデータ型も持っていることに注意してください。二重引用符で囲まれた値は常にストリングですが、引用符を持たない数値は数字と解釈されます。表 3 を見るとわかるように、ストリング述部は VARCHAR 型の XML 索引でないと評価できませんが、数値述部は DOUBLE 型の索引でしか評価できません。

リレーショナル索引のデータ型は、索引付けされた列の型によって決まります。しかし、DB2 は XML スキーマを強制的に XML 列に関連付けしないため、要素や属性のデータ型は事前に決まりません。従って、各 XML 索引にはターゲット型が必要です。そして、その型が重要なのです。例えば、price 要素が値 9 を持つと考えてみてください。ストリング述部 "9" < "29" は偽ですが、数値比較 9 < 29 は真です。これは、意味的に正しい数値比較が必要な場合には DOUBLE 索引を使うべきだ、ということを明確に示しています。つまり「price」要素は、DOUBLE として索引付けするのが最善と言えます。

表 3. XML 索引と述部でのデータ型
#述部または索引定義xmlpattern '/book/price' as sql double を使うxmlpattern '/book/price' as sql varchar(10) を使う
1ただし $i/book/price < "29"-+
2ただし $i/book/price < 29+-

XML 結合述部に索引を使う

前の例では、リテラル値を含む値述部を見てきました。こうしたリテラル値部によって比較のデータ型が決まります。そうした決め方は、通常は結合述部では不可能です。例えば、「authors」という表を考えてみてください。この表は詳細な著者情報を XML フォーマットで持ち、著者情報には著者 ID が含まれており、この著者 ID は book データの中にもあります。books 表の中にある本の著者の詳細データのみを取得するために、結合を使うことを考えてみましょう。下記のように、著者 ID に索引を使うと便利なようです。

create table books (bookinfo xml);
create table authors (authorinfo xml);

create index authorIdx1 on books(bookinfo) generate key using
xmlpattern '/book/authors/author/@id' as sql double;

create index authorIdx2 on authors(authorinfo) generate key using
xmlpattern '/author/@id' as sql double;

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO") 
    for $j in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO") 
where $i/book/authors/author/@id = $j/author/@id
return $j;

このクエリーは対象とする著者情報を取得しますが、結合処理には、どちらの索引も使いません。著者 ID に対する結合述部は、比較のためのデータ型を示すリテラル値を含んでいないことに注意してください。従って DB2 は、どのようなデータ型を持つ著者 ID とも照合しなければなりません。例えば、表 4 の book データと author データを考えてみてください。著者 John Doe は、値 (47) の数値 ID を持ちますが、著者 Tom Noodle は、値 (TN28) の非数値型 ID を持っています。どちらも、相手側の表では有効な一致があります。従って、結合の結果には両方を含める必要があります。しかし、もし DB2 が数値索引 authorIdx1 または authorIdx2 を使ったとすると、著者 ID「TN28」を見つけられず、不完全な結合結果を返します。従って DB2 はこれらの索引を使うことができず、正しいクエリー結果を得るためには、表スキャンという手段に訴えるしかありません。

book データの例
<book>
    <authors>
     <author  id="47">John Doe</author>
    </authors>
    <title>Database Systems</title>
    <price>29</price>
</book>

<author id="47">
    <name>John Doe</name>
     <addr>
           <street>555 Bailey Av</street>
           <city>San Jose</city>
           <country>USA</country>
      </addr>
      <phone>4084511234</phone>
</author>
author データの例
<book>
    <authors>
     <author  id="TN28">Tom Noodle</author>
    </authors>
    <title>International Pasta</title>
    <price>19.95</price>
</book>

<author id="TN28">
    <name>Tom Noodle</name>
     <addr>
           <street>213 Rigatoni Road</street>
           <city>Toronto</city>
           <country>Canada</country>
      </addr>
      <phone>4162050745</phone>
</author>

次のような 2 つの場合、DB2 では DOUBLE 索引を使うと便利です。

  • 非数値型の著者 ID が存在するものの、それを無視したい場合
  • 非数値型の著者 ID が存在しない場合

では、この 2 つの場合を調べてみましょう。

非数値型の著者 ID を無視したい場合、それをクエリーの中で示すには、DB2 で DOUBLE 型の索引を使うようにします。次のクエリーは、結合述部の両側を DOUBLE にするように、明示的にキャストしています。これによって数値比較を要求し、また非数値型の結合一致比較を明示的に不許可にしています。従って DB2 は、DOUBLE 索引を使って高速な結合処理を行うことができます。DOUBLE にキャストすると非数値型の値があった場合に失敗するため、述部を追加して、どちらの表にも非数値型の著者 ID を含めないようにする必要があります。例えば次のクエリーは、0 よりも大きな数値 ID しか許しません。

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO") 
    for $j in db2-fn:xmlcolumn ("AUTHORS.AUTHORINFO") 
where $i/book/authors/author/@id > 0
  and $j/author/@id > 0
  and $i/book/authors/author/@id/xs:double(.) = $j/author/@id/xs:double(.)  
return $j;

非数値型の著者 ID が存在しないことがわかっている場合には、こうした余分な述部は必要なく、クエリーは次のように単純になります (しかも、やはり DOUBLE 索引を使っています)。

XQUERY
for $i in db2-fn:xmlcolumn("BOOKS.BOOKINFO") 
    for $j in db2-fn:xmlcolumn ("AUTHORS.AUTHORINFO") 
where $i/book/authors/author/@id/xs:double(.) = $j/author/@id/xs:double(.)  
return $j;

このクエリーには、books 表と authors 表のどちらを制限する値述部も含まれていないため、すべての著者 ID を読み取るためには、DB2 は 2 つの表のどちらかを表スキャンする必要があります。そして各著者 ID に対して、その ID が相手側の表の中にあるかどうか、索引を使って調べます。これは、索引を使わずに 2 つの表スキャンをネスト・ループ結合するよりも、ずっと高速です。DB2 のコスト・ベースのオプティマイザーは、どちらの表をスキャンすべきか、どちらの表にアクセスすべきかを、索引を使って決定します。表 5 は、この 2 種類の実行計画を示しています。この 2 つの実行計画は、同じ結合を SQL/XML 表記を使って書く場合にも適用できますが、2 つの計画のどちらを使うかは、XMLEXISTS 述部のスタイルによって決まります。

クエリー 1:
select authorinfo
from books, authors
where xmlexists('$b/book/authors[author/@id/xs:double(.) = 
                    $a/author/@id/xs:double(.) ]'
                 passing bookinfo as "b", authorinfo as "a");
クエリー 2:
select authorinfo
from books, authors
where xmlexists('$a/author[@id/xs:double(.) = 
                    $b/book/authors/author/@id/xs:double(.) ]'
                 passing bookinfo as "b", authorinfo as "a");

どちらのクエリーの場合も、結合述部は大括弧の中で表現されます。クエリー 1 では、大括弧の中の述部は $b で始まる式に対する述部であり、索引が使われる books 表に対する述部を意味します。従ってDB2 は、まず authors に対して表スキャンを実行し、次に索引 AUTHORIDX1 を使って books 表を調べます。これを表 5 の左側に示します。

クエリー 2 の場合、結合条件は $a で始まる式に対する述部であり、これは authors 表に対する述部を意味します。従って DB2 は、まず books に対して表スキャンを実行し、次に索引 AUTHORIDX2 を使って authors 表を調べます (表 5 の右側)。従って、XMLEXISTS 述部の書き方は結合の順序に影響し、DB2 がどちらの表に対して (索引アクセスではなく) 表スキャンを使うかを決定します。もし表スキャンが避けられないのであれば、最も小さな表に対して行いたいものです。

db2exfmt で作成された、XML 結合クエリーに対する実行計画: クエリー 1
                      Rows
                     RETURN
                     (   1)
                      Cost
                       I/O
                       |
                  3.59881e-005
                     NLJOIN
                     (   2)
                     5410.62

                       743
                /-------+-------\
        1.29454e-007              278
           NLJOIN               NLJOIN
           (   3)               (   6)
           4311.96              1098.66
             570                  173
          /---+--\               /-+\
       556    2.32831e-010    139      2
     TBSCAN      XSCAN      FETCH   XSCAN
     (   4)      (   5)     (   7)  (  11)
     106.211     7.5643     47.237  7.56421
       14           1         34       1
       |                   /---+---\
       556              139          556
 TABLE: MATTHIAS      RIDSCN   TABLE: MATTHIAS
     AUTHORS          (   8)        BOOKS
                      15.2133
                         2
                        |
                        139
                      SORT
                      (   9)
                      15.2129
                         2
                        |
                        139
                      XISCAN
                      (  10)
                      15.1542
                         2
                        |
                        556
                  XMLIN: MATTHIAS
                    AUTHORIDX1
db2exfmt で作成された、XML 結合クエリーに対する実行計画: クエリー 2
                       Rows
                      RETURN
                      (   1)
                       Cost
                        I/O
                        |
                   8.37914e-015
                      NLJOIN
                      (   2)
                      5410.63
                        743
                /--------+--------\
        1.29454e-007           6.47269e-008
           NLJOIN                 NLJOIN
           (   3)                 (   6)
           4311.96                1098.67
             570                    173
          /---+--\               /---+--\
       556    2.32831e-010    139    4.65661e-010
     TBSCAN      XSCAN      FETCH       XSCAN
     (   4)      (   5)     (   7)      (  11)
     106.211     7.56429    47.2365     7.5643
       14           1         34           1
       |                   /---+---\
       556              139          556
 TABLE: MATTHIAS      RIDSCN   TABLE: MATTHIAS
      BOOKS           (   8)       AUTHORS
                      15.2128
                         2
                        |
                        139
                      SORT
                      (   9)
                      15.2124
                         2
                        |
                        139
                      XISCAN
                      (  10)
                      15.1537
                         2
                        |
                        556
                  XMLIN: MATTHIAS
                    AUTHORIDX2

XML 結合クエリーに関する助言を要約すると、使用すべき XML 索引の型に合わせて結合述部をキャストするようにします。そうしないと、クエリーのセマンティクスによって索引が使えなくなってしまいます。もし XML 索引が DOUBLE として定義されている場合は、xs:double として結合述部をキャストします。もし XML 索引が VARCHAR として定義されている場合には、fn:string として結合述部をキャストします。この一覧を表 6 に示します。

表 4. XML 索引を使えるように結合述部をキャストする
索引 SQL の型結合述部のキャストに使用する型コメント
doublexs:double任意の数値比較の場合
varchar(n), varchar hashedfn:string任意のストリング比較の場合
datexs:date日付比較の場合
timestampxs:dateTimeタイムスタンプ述部の場合

「between」述部に対する索引サポート

XQuery は、リレーショナルの「between」述部のような特別な関数や演算子を持っていません。また、存在の有無を判断するという XQuery の汎用比較述部の性質から、「between」条件を表現する場合には注意が必要です。

例えば、価格 (price) が 20 から 30 の間の本を探したいとします。直感的には、/book[price > 20 and price < 30] のような述部を使いたくなりますが、これでは「between述部」にはなりません。つまり /book/price に対する索引があった場合、DB2 は、20 から 30 の索引範囲スキャンを行ってその価格範囲の本を見つける、ということができません。これは、book 文書が、下記の例のように price という子を複数持っているかもしれないためです。

<book>
   <title>Database Systems</title>
   <price currency="RMB">40</price>
   <price currency="USD">10</price>
</book>

汎用比較 (> や <、=、<=、など) は、あるかないか、というセマンティクスを持っています。そのため、/book[price > 20 and price < 30] という述部は、ある book 要素が 20 よりも大きな値の子要素「price」を持っており、そして 30 よりも小さな値の子要素「price」を持っている場合に、その book 要素を選択します。これらは、同じ 1 つの子要素であるかもしれず、2 つの別々な「price」要素かもしれません。上記のサンプル文書には 20 よりも大きな price があり、また 30 よりも小さな (別の) price があるので、この述部を満足します。しかし、この 2 つのどちらも 20 と 30 の間にはありません。

もしDB2 が 20 から 30 という 1 つの索引範囲スキャンを使ったとすると、DB2 はこの文書を見落とし、不完全なクエリー結果を返します。それを避けるには、DB2 は 2 つの索引スキャンの論理積を計算する必要があり、これは多くの場合、大幅なコスト増となります。この実行計画の違いを図 3 に示します。左側の実行計画は、サンプル文書を捉えるために DB2 が考慮すべき IXAND (index AND'ing) 計画を示しています。この計画は、どちらの XISCAN も非常に大量の行 ID を生成する可能性があり、その多くを XISCAN の上にある IXAND 演算子で削除する必要があるため、効率的ではありません。これは、多くの本が $20 以上であり、また多くの本が $30 以下であるためです。実際、両方の XISCAN を組み合わせることによって、表の中にある行の数よりも多くの行が生成されてしまいます。これは IXAND 演算子にとって負荷が重い作業であり、しかも見つけられる論理積は、おそらく小さなものです。

もし、真の「between」述部が必要な場合には、右側の実行計画の方がずっと適切です。その理由は、先頭の述部から開始する 1 つの範囲スキャンによって、一致する行 ID のみが得られるためです。こちらの方が索引アクセスはずっと少なく、しかも IXAND がありません。そのため述部の選び方によっては、全体的なパフォーマンスが 1 桁か 2 桁良くなります。

一対の範囲述部の評価に対して、IXAND を使う場合と単一の範囲スキャンを使う場合
                     RETURN 
                       |
                     NLJOIN 
                       |
                     /-+-\
                    /     \
                 FETCH    XSCAN  
                   |
               /---+---\
              /         \
          RIDSCN       TABLE:   
            |	      BOOKS
          SORT   
            |
          IXAND  
            |
        /---+---\
    XISCAN      XISCAN
 price > 20    price < 30




                  RETURN 
                    |
                  NLJOIN 
                    |
                  /-+-\
                 /     \
              FETCH    XSCAN  
                |
            /---+---\
           /         \
       RIDSCN       TABLE:   
         |	          BOOKS
       SORT   
         |
     XISCAN
 20 < price < 30

同じデータ項目に対して範囲述部が一対となっている場合、DB2 がそのデータ項目を複数項目の連続ではなく 1 つのものと判断すれば、DB2 コンパイラーはそれを「between」と解釈し、1 つの索引範囲スキャンで評価します。つまり述部は、同じ 1 つの項目に必ず between の両側 (> と <) が適用されるように構成する必要があります。これは、値比較 (> と < と =) や self 軸、属性などによって実現できます。

値比較
もし book が最大でも 1 つの price 要素しか持たないことがわかっている場合には、XQuery の値比較を使ってクエリーを書くことができます。これによって、比較のオペランドを強制的に 1 つにすることができます。例えば、/book[price > 20 and price < 30] は安全に「between」と解釈でき、price 索引による 1 つの範囲スキャンで評価できます。もし複数の price 子要素を持つ book があった場合には、このクエリーは実行時にエラーとなって失敗します。

self 軸
値比較の代わりに、self 軸 (ドット「.」で表記されます) を使って「between」述部を表現することができます。/book/price[. > 20 and . < 30] という式の中の self 軸によって、両方の述部が同じ price 要素に適用されます。self 軸は必ず 1 つのものとして評価されるため、この式で between 述部を構成できることになります。この述部では 1 つの book が複数の price を持つことができますが、すべての price が 20 から 30 の間にある必要があります。値比較に比べて有利な点は、ランタイム・エラーの危険がないことです。

属性
もし book の price が属性の場合は、1 つの book 要素に対して最大でも price は 1 つしかありません。式 /book[@price>20 and @price<30] の中では、範囲述部のオペランドは 1 つのものなので、DB2 は 1 回の索引範囲スキャンを行うだけで「between」を評価することができます。


テキスト・ノードと XPath ステップ「/text()」を索引付けする

ここでちょっと、テキスト・ノードとは何かを思い出してみてください。図 4 は、XML データ・モデルでのサンプル文書と、その階層構造フォーマットを示しています。各要素は要素ノードによって表現され、実際のデータ値は、テキスト・ノードによって表現されます。XML データ・モデルでは、ある要素の値は、その要素の下にあるサブツリーの全テキスト・ノードを連結したものとして定義されます。従って、要素「book」の値は「Database Systems29」です。最下位レベルにある要素の値は、その要素のテキスト・ノードと同じです。例えば要素「price」の値は「29」です。

<book>
   <title>Database Systems</title>
   <price>29</price>
</book>
図 3: サンプル文書の XML データ・モデル

XPath 式 /book/price と /book/price/text() とは異なります。前者は要素ノード「price」を特定し、後者は値「29」を持つテキスト・ノードを指します。従って、次の 2 つのクエリーは異なる結果を返します。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
return $b/price

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
return $b/price/text()

最初のクエリーは完全な要素ノード (つまり <price>29</price>) を返しますが、2 番目のクエリーはテキスト・ノード値 29 しか返しません。もし、/text() を持たない XPath 式 (例えば下記のクエリーの $b/book など) がクエリー述部の中で使われていると、DB2 は自動的に要素の値を使って述部を評価します。「book」要素の値は「Database Systems29」なので、このクエリーは、サンプル文書を有効な一致として返します。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book = "Database Systems29"
return $b

しかし、次のクエリー (where 節のパスに /text() が追加されています) はサンプル文書を返しません。これは、要素「book」の直下にはテキスト・ノードがないためです。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/text()= "Database Systems29"
return $b

つまり一般的には、クエリーのセマンティクスは /text() の使い方で変わってきます。1 つのテキスト・ノードしか含まない、最下位レベルの要素は、/text() があってもなくても同じ動作をするかもしれません。例えば、クエリー実行中に見つかったすべての「price」要素が 1 つのテキスト・ノードしか持っておらず、他に子ノードを持っていない場合には、次の 2 つのクエリーは同じ結果を返すかもしれません。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/price < 10
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")
where $b/book/price/text() < 10
return $b

一般的には /text() によってクエリーのセマンティクスが変わるため、索引が適切かどうかも /text() によって変わります。表 7 を見ると、/text() を持つ述部を評価するには、XML パターンの中の /text() も指定する索引を使う必要があることがわかります。もしその索引が /text() を使っていなければ、述部にも /text() を使うべきではありません。

表 5. 索引と述部に /text() を持つ場合と持たない場合
述部または索引定義xmlpattern '/book/title/text()' as sql varchar(128) を使うxmlpattern '/book/title' as sql varchar(128) を使う
ただし $i/book/title = "Database Systems"-+
ただし $i/book/title/text() = "Database Systems"+-

単純にするために、XML 索引定義の中でもクエリー述部の中でも /text() を使わない方が無難です。XML パターン //text() に索引を定義することによって、/text() で終わるすべてのパス式の述部をサポートしたくなりますが、そうした索引は、XML 列のすべての文書の、すべてのテキスト・ノード値を含むことになります。従って索引は非常に大きくなり、挿入や更新、削除などの操作を行う際に維持管理コストがかかります。こうした索引は、アプリケーションがほとんど読み取り専用であり、どの要素が検索条件に使われるか本当に予測できない場合以外は、一般的には避けた方が無難です。


非リーフ要素を索引付けする

上のセクションでは、要素 /book に対する述部として、非リーフ (またはアトミックでない) 要素と呼ばれる要素を見ました (他の要素を含むことから、このように呼ばれます)。非リーフ要素に索引を定義することはできますが、あまり使い道はありません。次のような XML 文書を考えてみてください。XML パターン /book に対する索引は、この文書に対する 1 つの索引エントリーを含んでおり、その索引エントリーの値は「John DoePeter PanDatabase Systems29SQLrelational」です。クエリーは通常、このように連結された値を述部に持つことはないので、こうした索引には使い道がありません。ほとんどの索引は、必ずリーフ要素に付けられます。

<book>
   <authors>
      <author id="47">John Doe</author>
      <author id="58">Peter Pan</author>
   </authors>
   <title>Database Systems</title>
   <price>29</price>
   <keywords>
      <keyword>SQL</keyword>
      <keyword>relational</keyword>
   </keywords>
</book>

ただし、非リーフ要素に索引を使った方がよい場合もあります。例えば、市外局番 (area code) に対する述部と完全な電話番号に対する述部を含んだクエリーを考えてみてください。この場合、phone 要素を、次の文書のように設計することができます。

<author  id="47">
    <name>John Doe</name>
    <phone>
           <areacode>408</areacode>
           <number>4511234</number>
    </phone>
</author>

そうすると、非リーフ要素「phone」に対して XML 索引を 1 つ、要素「areacode」に対して XML 索引を 1 つ定義することができます。

create index phoneidx on authors(authorinfo) generate key using
xmlpattern '/author/phone' as sql double;

create index areaidx on authors(authorinfo) generate key using
xmlpattern '/author/phone/areacode' as sql double;

このようにすれば、次のどちらのクエリーでも索引アクセスを使え、表スキャンを使わずに済みます。

select authorinfo from authors
where xmlexists('$a/author[phone=4084511234]' passing authorinfo as "a");

select authorinfo from authors
where xmlexists('$a/author[phone/areacode=408]' passing authorinfo as "a");

XML 索引を、複数列のリレーショナル索引のような複合キー索引にすることはできません。つまり、2 つ以上の XML パターンに対して 1 つの索引を定義することはできません。しかし、もし要素が適切にネストされていれば、複合索引のような使い方をすることができます。例えば、上記の索引 phoneidx は、/phone/areacode と /phone/number に対する複合索引とほとんど同じように動作します。


XML 索引が使えない特別な場合

XMLQUERY と XMLEXISTS に関する特例

XML 索引の適切さに関してこれまで説明してきたガイドラインは、XQuery や SQL/XML クエリーにも適用できます。この他に、SQL/XML 関数 XMLQUERY と XMLEXISTS に関して、特に考慮すべきことがあります。

SQL 文の select 節の XMLQUERY 関数の中で XML 述部を使ったとしても、結果セットから行を削除することはできません。従って索引が使えません。XML 述部は 1 度に 1 つの文書にしか適用できず、しかも文書の一部断片しか返さないかもしれません (場合によっては空の断片が返るかもしれません)。つまり文書や行をフィルターするための述部は、SQL/XML 文の where 節の XMLEXISTS 述部に置く必要があります。

XMLEXISTS の中で述部を表現する場合には、必ず大括弧を使って $a/author[phone=4084511234] のようにし、$a/author/phone=4084511234 のようにはしません。この 2 つの述部のうち、後者はブール述部であり、もし求める値を phone 要素が持っていない場合には「偽」を返します。XMLEXISTS は、ある値が存在するかどうかを正確にチェックするため、たとえ「偽」という値であっても値が存在すれば XMLEXISTS を満足することになり、どんな文書も結果セットとして適格ということになります。大括弧を使うことによって、XPath 式は空シーケンスと評価され、有無を調べる存在テストは失敗し、(もし文書の中に求める電話番号がなければ) その文書に対応する行を削除します。

こうした XMLQUERY や XMLEXISTS のセマンティクスに関する詳細な例については、「DB2 9 での pureXML パフォーマンスのための 15 のベスト・プラクティス」(developerWorks、2006 年 10 月) を参照してください。

Let 節と return 節

XQuery の let 節と return 節の述部は、結果セットをフィルターしないことに注意してください。従って、要素の構造が関係する場合には、こうした述部に索引を使うことはできません。次の 2 つのクエリーは、索引を使うことができません。これは、すべての author に要素「phone408」を返す必要があり、たとえそれが 408 という市外局番区域外の author にとって空要素であっても例外ではないためです。

XQUERY for $a in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO")/author
let $p := $a/phone[areacode="408"]//text()
return <phone408>{$p}</phone408>

XQUERY for $a in db2-fn:xmlcolumn("AUTHORS.AUTHORINFO")/author
return <phone408>{$a/phone[areacode="408"]//text()}</phone408>

親ステップ

また DB2 9 は、親ステップ ("..") の下にある述部には索引を使いません。例えば次の 2 つのクエリーでの「price」に対する述部のような場合です。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/title[../price < 10]
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/title 
where $b/../price < 10
return $b

このような述部は parent 軸を使わなくても表現できるため (下記)、これは大きな制約ではありません。

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book[price < 10]/title
return $b

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book
where $b/price < 10
return $b/title

ダブル・スラッシュ (//) を持つ、特別な場合

もう 1 つ、注意すべき場合として、子孫あるいは self 軸を持つ述部 (一般的に // と省略されます) を使う場合があげられます。例えば、ID 129 を持つ author の本を探したいとします。もし author ID 属性が複数レベルにあると、あるいは author ID 属性がどのレベルにあるのか、どの要素の下にあるのか不明な場合には、次のような嘆かわしいクエリーを書くことになるかもしれません。

これは間違いです

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/authors[//@id = 129]    
return $b

このクエリーの意図は、「authors」要素のどこかにある、あるいは authors 要素の下のどこかにある ID 属性を探すことです。しかし、大括弧の中の述部の前にあるスラッシュまたはダブル・スラッシュ ( / または // ) はコンテキストを持たないため、文書のルートを参照します。その結果、このクエリーは結果として次の文書を返しますが、これは意図したものとは違っています。

<book id="129">
    <authors>
      <author  id="47">John Doe</author>
      <author  id="58">Peter Pan</author>
    </authors>
    <title>Database Systems</title>
    <price>29</price>
</book>

これを避けるためには、ドット (self 軸) を追加して、文書ツリーの中の「authors」要素から下の子孫、または self 軸 (//) に適用したいという意図を示します。

これは正しい

XQUERY for $b in db2-fn:xmlcolumn("BOOKS.BOOKINFO")/book/authors[.//@id = 129]  
return $b

またこれによって DB2 は、/book//@id または //@id に定義される索引も使えるようになりますが、ドットがないと、索引を使えません。

索引の適切さが XQuery と SQL/XML 言語のセマンティクスによって影響を受ける例は、記事「On the Path to Efficient XML Queries」でも説明されています。


まとめ

XML 索引は、XML クエリーのパフォーマンスを高めるために必須ですが、ワイルドカード、名前空間、データ型、結合、テキスト・ノード、そして XML クエリーの持つその他のセマンティックな面によって、あるタイプの索引が使えるか、あるいは使えないかが決まります。また、XML 索引定義とクエリー述部とが対応しているかどうか、よく注意する必要があります。この記事では、一連のガイドラインと例を示しながら、XML 索引を使うことで表スキャンが避けられ、またクエリーのパフォーマンスを高められることを示しました。最も重要なガイドラインは、ダウンロードできる cheat sheet (虎の巻) の中に要約されています。


謝辞

この記事の執筆にあたって協力くださった、Andrey Balmin と Kevin Beyer、Christina Lee、Henrik Loeser、Fatma Ozcan、Bryan Patterson、Vitor Rodrigues、Marcus Roy、そして Cindy Saracco の各氏に感謝いたします。


ダウンロード

内容ファイル名サイズ
Cheat Sheetcheat_sheet.pdf56KB
DB2 XML Index Exploitation - DDLandQueries.txtDDLandQueries.txt8KB

参考文献

学ぶために

製品や技術を入手するために

  • IBM ソフトウェア試用版 を利用して皆さんの次期開発プロジェクトを構築してください。developerWorks から直接ダウンロードすることができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, XML
ArticleID=272870
ArticleTitle=DB2 9 での XML クエリー・パフォーマンスを高める XML 索引を学ぶ
publish-date=11022006