はじめに
これまでいくつかチューニングの記事を挙げてきましたが、今回は問い合わせ言語「XQuery/XPath」についてのチューニング手法を挙げていきたいと思います。性能チューニングで行き詰ったときはまず試してもらいたいものを列挙していきます。
「//」というパス表現はなるべく避けよう
あるXML形式変数$aからsaleSum要素を取り出すとき、以下の2パターンのXPathが考えられます。
パターン①・・・$a/repeat/info/saleSum
パターン②・・・$a//saleSum
慣れていない人のプログラミングではついついシンプルに見えるパターン②の記述を採用するのを見かけますが、「//」というXPathは$aの子孫の要素すべてを評価対象にします。また仕様変更でsaleSum要素が全く別の意味で追加されると、従来まであったクエリの戻り値が仕様と異なるものになる恐れもあるので、必ずパターン①のように要素名を書くことをお奨めします。
子ノードをたどるときは「node()」より「*」を使ってみる
取得対象の要素名が変数だった場合、以下の2パターンのXPathが考えられます。
パターン①・・・$a/*[fn:name() = $elementName]/text()
パターン②・・・$a/node()[fn:name() = $elementName]/text()
この2個のノードテスト記述は以下のような微妙な違いがあります。
パス表現 | 取得対象 |
---|---|
* | 主ノード型の全てのノード |
node() | 全てのノード |
「*」は軸となっているノードの種類を要素・属性・名前空間と判別して、これに限定したノードテストを行います。要素のみを相手にすればよい場合、「*」を使う方が良いようです。
複数の子ノードを取得するコストをハッシュマップで抑える
パス表現は、例えば$a/sampleと明確に要素名をしていた場合でも、$aの全ての子ノードの名前を「sample」と照合します。このため$aから複数の子ノードを参照する場合、何度も同じ照合を受ける要素が出てきます。では、初めに子ノードを全て確認してハッシュマップに入れておけばどうでしょうか?2個目の要素からはパス取得せず、ハッシュマップを参照すればよくなり無駄な照合が省けそうです。
実は通常のXQueryではハッシュマップのデータ型はなかったりしますが、MarkLogicにはmap:map型というハッシュマップを実現するデータ構造があります。こちらに使い方などの説明があります。
適用方法や効果は、以前に書いた記事を参照して頂ければ思います。
本項ではXQuery/XPathを対象にしていますが、例えばJava上で行う無索引のXMLパースでは、この考えがさらに性能向上効果をもたらします。
おわりに
RDBより複雑な構造を持つXMLに対する問い合わせ言語であるXQuery/XPathは、「子だけなのか?子孫すべてなのか?」など選択の余地が幅広いのが特徴です。今回は、多用されかつ修正も容易そうなパス表現を対象にしました。性能問題に対応するときは、まずはこの辺りを見ることをお奨めします。
\def\textsmall#1{%
{\rm\scriptsize #1}
}
免責事項
$\textsmall{当ユーザ会は本文書及びその内容に関して、いかなる保証もするものではありません。}$
$\textsmall{万一、本文書の内容に誤りがあった場合でも当ユーザ会は一切責任を負いかねます。}$
$\textsmall{また、本文書に記載されている事項は予告なしに変更または削除されることがありますので、予めご了承ください。}$