レベル: 中級 Matthias Nicola, Senior Software Engineer, IBM Silicon Valley Lab Martin Sommerlandt, DB2 pureXML, IBM
2008年 11月 13日 XML の要素や属性の値は、その定義から、大文字小文字を区別します。例えば <city> 要素を検索して「Paris」という値を見つける場合には、「PARIS」または「paris」を見つけることはできません。この問題は fn:upper-case() のような XQuery 関数を使えば解決することができますが、そうした関数を使うと XML 索引が使えないため、パフォーマンスが必ずしも最適にはなりません。この記事では、DB2® pureXML™ を使って大文字小文字を区別しないデータベースを作成する方法と、XML クエリーと XML 索引に対して要求される動作について説明します。
upper-case 関数と lower- case 関数による、大文字小文字を区別しない検索
次の例を見ると、大文字小文字を区別しない検索をよく理解できるはずです。リスト 1 は INTEGER 列と XML 列を持つテーブルを定義し、その表に 7 行のデータを挿入しています。各行には <city> という XML 要素を含む簡単な顧客情報が含まれています。
この要素の値は、大文字小文字を区別して考えるとそれぞれ異なっています。ある値はすべて大文字であり、ある値はすべて小文字であり、そしてまたある値は大文字と小文字が混在 (最初の文字が大文字) しています。こうしたことが起こるのは、データ入力の際の大文字小文字のルールが異なる、さまざまなアプリケーションのデータを使用する場合です。
リスト 1. テーブルとデータの例
CREATE TABLE customer (id INTEGER, xmldoc XML);
INSERT INTO customer (id, xmldoc)
VALUES (1,'<Customer id="1"><city>PARIS</city></Customer>'),
(2,'<Customer id="2"><city>Tokyo</city></Customer>'),
(3,'<Customer id="3"><city>tokyo</city></Customer>'),
(4,'<Customer id="4"><city>PARIS</city></Customer>'),
(5,'<Customer id="5"><city>paris</city></Customer>'),
(6,'<Customer id="6"><city>Delhi</city></Customer>'),
(7,'<Customer id="7"><city>Paris</city></Customer>'); |
これらの XML 文書に対してクエリーを実行して特定の都市の顧客を見つけるアプリケーションには、おそらく大文字小文字を区別しない検索が必要なはずです。例えば、パリの顧客をすべて見つけるために 1、4、5、7 行を取得したいとします。しかし、「Paris」という値を検索すると、7 行目しか返されません。必要な 4 行すべてを取得するためには、XQuery 関数の fn:upper-case() を使って city 要素の値を大文字に変換し、それらを「PARIS」と比較します。これを実現したのがリスト 2 のクエリーであり、このクエリーではパリの顧客 4 人すべてを返します。
リスト 2. パリの顧客を選択する
SELECT id, XMLCAST( XMLQUERY('$XMLDOC/Customer/city') AS VARCHAR(15)) AS city
FROM customer
WHERE XMLEXISTS('$XMLDOC/Customer[fn:upper-case(city) = "PARIS"]'); |
クエリーで検索値に対するパラメーター・マーカーを使う場合には、そのパラメーターも大文字に変換する必要があります。これをリスト 3 に示します。パラメーター・マーカー ("?") には VARCHAR(15) として入力し、それを変数「c」として XQuery の述部に渡します。
リスト 3. パラメーター・マーカーを使って顧客を選択する
SELECT id, XMLCAST( XMLQUERY('$XMLDOC/Customer/city') AS VARCHAR(15)) AS city
FROM customer
WHERE XMLEXISTS('$XMLDOC/Customer[fn:upper-case(city) = fn:upper-case($c)]'
PASSING CAST(? AS VARCHAR(15)) AS "c"); |
図 1 は上記のサンプル・クエリーの出力を示しています。
図 1. サンプル・クエリーの結果
この方法がうまく動作するのは、少量のデータにしかクエリーを実行しない場合や、クエリーの中に他の選択性の高い述語も含まれ、小さな中間結果セットのみに大文字の述部が適用される場合です。問題は、fn:upper-case() 関数を持つ述部があると DB2 で XML 索引を使用できなくなることです。従って、この方法は大量のデータがある場合には適切ではありません。
fn:upper-case() 関数を使わないようにし、XML 索引のメリットを活用するためには、大文字小文字を区別しないデータベースを作成する必要があります。
大文字小文字を区別しないデータベースを DB2 で作成する
DB2 は Version 9.5 Fixpack 1 から、ロケールに対応する Unicode 照合をサポートしています。これを利用すると、大文字小文字の区別やアクセント記号を無視することができます。すべてのストリングの比較において大文字小文字を区別しないデータベースを作成するためには、UCA500R1 照合を使う必要があります (リスト 4)。
リスト 4. 大文字小文字を区別しないデータベースを作成する
CREATE DATABASE testdb
USING CODESET UTF-8 TERRITORY US
COLLATE USING UCA500R1_LEN_S2; |
UCA500R1_LEN_S2 というストリングは正確に言うと何を意味するのでしょう。UCA500R1 は、このデータベースでは Unicode 標準のバージョン 5.0.0 に基づくデフォルトの UCA (Unicode Collation Algorithm: Unicode 照合アルゴリズム) を使うように指定しています。デフォルトの UCA は Unicode がサポートしているすべての言語の照合シーケンスを同時にサポートすることはできないため、オプションとしての属性を使って文字の並び替えをカスタマイズします。属性同士の区切りにはアンダースコアー (_) を使います。UCA500R1 キーワードに任意の属性を追加すると UCA 照合名を構成することができます。
リスト 4 に使われている照合名には、LEN と S2 という 2 つの属性が含まれています。LEN は、L (language: 言語) と EN (ISO 639-1 による英語の言語コード) を連結したものです。2 番目の属性である S2 は強度レベルを指定します (強度レベルによって、ストリングの並び替えや比較の際に大文字小文字の区別やアクセント記号を考慮するかどうかが決まります)。リスト 4 では強度レベル 2 が使われているため、「PARIS」と「paris」は等価です。下記は考えられる他の値の例です。
UCA500R1_LEN_S1 ならば 「cliche」=「Cliche」= 「cliché」です。
UCA500R1_LEN_S2 ならば「cliche」=「Cliche」<「cliché」です。
UCA500R1_LEN_S3 なばら「cliche」<「Cliche」<「cliché」です。
UCA 照合名として使用できるすべての組み合わせの一覧が DB2 インフォメーション・センター (「参考文献」) に用意されています。
大文字小文字を区別しないデータベースで XML データに対してクエリーを実行する
強度レベル 2 を持つ照合名 UCA500R1 を使ってデータベースを作成することができたので、今度は先ほどのクエリーを、あたかもすべてのデータが実際には大文字であるかのようにして (fn:upper-case() 関数を使わずに) 簡単に作成することができます (リスト 5)。さらには、検索ストリングが「Paris」でも「PARIS」でも、あるいは他の大文字小文字の組み合わせであっても構いません。
リスト 5. パリの顧客を選択する
SELECT id, XMLCAST( XMLQUERY('$XMLDOC/Customer/city') AS VARCHAR(15)) AS city
FROM customer
WHERE XMLEXISTS('$XMLDOC/Customer[city = "PARIS"]'); |
図 2. サンプル・クエリーの結果
抽出された都市の値の順に並べ替えるために ORDER BY 節を追加した場合も、やはり結果セットは同じままであり、PARIS、paris、Paris は同じ値として扱われます。
このデータに対して効率的にクエリーを実行するためには (特にテーブルの行数が多い場合には)、XPath /Customer/city に XML 索引を作成する必要があります (リスト 6)。
リスト 6. XML 索引を作成する
CREATE INDEX customer_lang_idx ON test (xmldoc)
GENERATE KEY USING XMLPATTERN '/Customer/city' AS SQL VARCHAR(15); |
さて、(Visual Explain、あるいは db2exfmt を使って) クエリーを分析すると、大文字小文字を区別しない検索に索引が使われていることがわかります。
図 3. 大文字小文字を区別しないデータベースにおいて、パリの全顧客に対してクエリーを実行するプランの分析
このセクションで説明した方法に潜む欠点は、データベース全体の中に存在している、すべてのテーブルのすべての列のすべてのデータが大文字小文字を区別せずに扱われる点です。特定のテーブルや列のみ大文字小文字を区別しない、ということはできません。すべてに対して大文字小文字を区別するか、あるいはまったく区別しないか、どちらか一方しかありません。
大文字小文字を区別しない動作は要素と属性の値のみに有効であり、タグ名そのものには有効ではないことに注意してください。XML タグとパス式は相変わらず大文字小文字を区別します。例えば、/Customer/city (小文字の「c」) と /Customer/City (大文字の「C」) という 2 つの XPath 式は別の式です。後者の式を使用した場合には、この記事のサンプル・データからは要素を見つけられないはずです。これはサンプル・データの中にある <city> 要素のスペルが小文字であるためです。
パフォーマンス
データベースに対してカスタマイズした照合を使用すると、条件の緩い UCA 設定を選択した場合にはストリングの突き合わせ回数が増える可能性があるため、クエリーのパフォーマンスに影響するかもしれません。言い換えると、大文字小文字を区別しないデータベースの方がストリング比較のコストが少し高くなるかもしれません。大文字小文字を区別するデータベースと区別しないデータベースでクエリーのパフォーマンスに違いがあるかどうかを調べるために、私達は通常の (大文字小文字を区別する) データベースと大文字小文字を区別しないデータベースを作成しました。私達は TPoX ベンチマークから 20,000 件の CustAcc 文書を挿入し、両方のデータベースでさまざまなクエリーを測定しました。
小から中程度の行数しか検索しないクエリーでは、2 つのテスト・データベースの間のパフォーマンスの違いは無視できる程度でした。大量の行数を検索するクエリー (例えば 20,000 件の XML 文書すべてに対してテーブル・スキャンを行い、すべての文書でストリングの比較を行う場合など) では、大きなパフォーマンスの差が見られました。そうしたクエリーの場合、大文字小文字を区別しないデータベースの方が 5% から 8% 長く時間がかかりました。これは大文字小文字を区別しない検索のための少しばかりの代償です。
まとめ
大文字小文字を区別せずに DB2 のデータを検索する方法には、生成列を使う方法 (「参考文献」を参照) など、いくつかの方法があります。そうした方法はリレーショナル・データには有効ですが、XML データに対してクエリーを実行するのには適していません。大文字小文字を区別せずに XML データを扱うためには、カスタマイズした Unicode 照合を使うデータベースを作成する方法がベストです。そうすることによって、データベースでのすべてのストリング値の比較で大文字小文字が区別されなくなり、XML 索引やリレーショナル索引を通常どおり使用できるようになります。また (大文字小文字の区別とアクセントに関して) 検索パターンが増えることによるオーバーヘッドは非常に低いことがわかりました。
参考文献
著者について  | 
|  | Matthias Nicola は IBM Silicon Valley Lab の DB2 pureXML™ の Senior Software Engineer です。彼は XQuery や SQL/XML、ストレージ、索引付けとパフォーマンスなど、DB2 での XML のあらゆる側面に焦点を当てた業務を行っています。彼は顧客やビジネス・パートナーと密接に協力しながら XML ソリューションの設計や実装、最適化の支援を行っています。IBM に入社する前には Informix Software でデータ・ウェアハウスのパフォーマンスに関する業務を行っていました。彼はドイツの Technical University of Aachen でコンピューター・サイエンスの博士号を取得しています。 |
 | 
|  | Martin Sommerlandt は IBM Silicon Valley Lab で 18 カ月間のインターンとして業務を行っています。彼はドイツの University of Technology Dresden でコンピューター・サイエンスを学んでおり、2007年に DB2 pureXML™ ソリューションに従事するインターンとして IBM に参加しました。彼の関心分野は XQuery や SQL/XML、XML のパフォーマンス、カスタム XML ソリューションなどです。彼は IBM 認定の DB2 Application Developer であり、また IBM 認定の DB2 Database Administrator です。また彼は DB2 の管理とプログラミングのチューターとしても働いています。 |
記事の評価
|