XML の推奨事項

SQL Server 2005 では XML データ処理を幅広くサポートしています。XML 値は XML スキーマ コレクションに従って型指定できる xml データ型の列にネイティブに保存できます。また、型指定しない状態でも保存できます。XML 列にはインデックスを設定できます。さらに、XQuery や XML DML を使用することで、きめ細かいデータ操作が実現されます。XML DML はデータ変更のための拡張機能です。

SQL Server 2000 や SQLXML Web リリースにも強力な XML データ管理機能が実装されていますが、リレーショナル データと XML データのマッピングに重点が置かれています。AXSD (注釈付き XSD) を使用してリレーショナル データの XML ビューを定義し、XML 中心の方式で XML データの一括読み込み、クエリ、および更新をサポートできます。さらに Transact-SQL 拡張機能によって、SQL 中心の方式で FOR XML を使用してリレーショナル クエリの結果を XML にマップしたり、OPENXML を使用して XML からリレーショナル ビューを生成できます。SQL Server 2005 では、これら 2 つの方式のサポートが拡張されました。SQL Server 2005 では、新しく追加されたネイティブ XML サポートと併せて、部分的に構造化されたデータや構造化されていないデータを管理する多機能アプリケーションを開発するための強力なプラットフォームが用意されています。

このトピックでは、SQL Server 2005 における XML のデータ モデリングと使用方法のガイドラインについて説明します。トピックは次の各セクションに分かれています。

  • データ モデリング
    SQL Server 2005 では、ネイティブ xml データ型や複数のテーブルに細分化した XML を使用することで、XML データをさまざまな方法で格納できます。ここでは、XML データを適切にモデル化するためのガイドラインについて説明します。XML データのインデックス設定、プロパティの昇格、XML インスタンスの型指定についても説明しています。
  • 使用方法
    サーバーに XML データを読み込む、クエリのコンパイル時に型を推定するなど、使用方法について説明します。また、密接に関連する機能の違いについて説明し、それぞれに適切な使用方法を提案します。説明には例を付けています。

データ モデリング

ここでは、SQL Server 2005 で XML の使用を推奨する理由を概説します。また、ネイティブ XML ストレージと XML ビュー テクノロジのどちらを選択するかについてのガイドライン、およびデータ モデリングのヒントについても説明します。

リレーショナル データ モデルと XML データ モデル

使用するデータが既知のスキーマにより十分に構造化されている場合、データ ストレージとしてはリレーショナル モデルが最適です。必要な機能やツールは SQL Server に用意されています。ただし、データが構造化されていないか構造化が部分的である場合、または構造化の状態が不明な場合は、データのモデリングを検討する必要があります。

構造や意味によるマークアップを行ってデータを移行できるようにするために、プラットフォームに依存しないモデルが必要な場合、XML が適しています。また、次に示す条件に該当する場合も XML が適切です。

  • データの密度が低いか構造が不明な場合。または将来のデータの構造が大きく変わる可能性がある場合。
  • データがエンティティ間の参照ではなく包含階層を成していて、再帰的な性質がある場合。
  • データの順序が固定している場合。
  • データの構造を基にして、データへのクエリやデータの部分的な更新を行う場合。

上記の条件のいずれにも該当しない場合は、リレーショナル データ モデルを使用してください。たとえば、データが XML 形式であってもデータの格納と取得にしかデータベースを使用しない場合、[n]varchar(max) 列で十分です。XML 列にデータを格納すると、それ以外の利点があります。たとえば、データが整形式であり有効であることをデータベース エンジンで判断できることや、XML データに対するきめ細かいクエリや更新がサポートされることなどです。

SQL Server 2005 で XML データを保存する理由

次に、ファイル システムによる XML データの管理ではなく、SQL Server 2005 のネイティブ XML 機能を使用する理由を示します。

  • XML データの共有、クエリ、および変更をトランザクション方式で効率的に行うため。アプリケーションにとって、きめ細かいデータ アクセスは重要です。たとえば、XML ドキュメントのセクションの一部を抽出したり、ドキュメント全体を置き換えることなく新しいセクションを挿入することができます。
  • リレーショナル データと XML データがあり、アプリケーションで双方のデータ間の相互運用性が必要なため。
  • XML とリレーショナルの 2 つの領域にまたがるアプリケーションで、クエリやデータ変更に対する言語サポートが必要なため。
  • データが整形式であることの保証、および必要に応じて XML スキーマに従ったデータの検証をサーバーで行うため。
  • クエリの処理を効率化し、スケーラビリティを高めるため XML データにインデックスを設定し、特に優れたクエリ オプティマイザを使用するため。
  • SOAP、ADO.NET、および OLE DB で XML データにアクセスするため。
  • XML データの管理にデータベース サーバーの管理機能を使用するため。たとえば、バックアップ、復旧、およびレプリケーションなどです。

上記の条件のいずれにも該当しない場合、XML 以外のラージ オブジェクト型 ([n]varchar(max)varbinary(max) など) でデータを保存するのが適切です。

XML ストレージ オプション

次に、SQL Server 2005 での XML のストレージ オプションを示します。

  • xml データ型としてのネイティブ ストレージ
    データの XML コンテンツを保持できる内部表現を使用してデータが保存されます。保持される XML コンテンツには、包含階層、表示順、要素や属性の値などがあります。具体的には、XML データの InfoSet コンテンツが保持されます。InfoSet の詳細については、http://www.w3.org/TR/xml-infoset を参照してください。InfoSet コンテンツでは、重要でない空白文字、属性の順序、名前空間プレフィックス、および XML 宣言が保持されないので、テキスト形式の XML のまったく同一のコピーにはならない場合があります。
    型指定された xml データ型、つまり XML スキーマにバインドされた xml データ型の場合、PSVI (スキーマ検証後の InfoSet) によって型情報が InfoSet に追加され、内部表現にエンコードされます。その結果、解析速度が大幅に向上します。詳細については、http://www.w3.org/TR/xmlschema-1 および http://www.w3.org/TR/xmlschema-2 で W3C XML スキーマの仕様を参照してください。
  • XML ストレージとリレーショナル ストレージのマッピング
    AXSD (注釈付きスキーマ) を使用することで、XML は 1 つ以上のテーブルの複数の列に分解されます。分解されても、リレーショナル レベルでのデータの忠実性は保たれます。したがって、要素間の順序は無視されますが階層構造は保持されます。再帰的なスキーマは使用できません。
  • ラージ オブジェクト ストレージ、[n]varchar(max)varbinary(max)
    データの完全なコピーが保存されます。これは、法務文書など、特殊な用途に使用します。ほとんどの場合、正確なコピーは不要であり、XML コンテンツ (InfoSet レベルの忠実性) で十分です。

一般的には、上記のいくつかの方法を組み合わせることができます。たとえば、xml データ型の列に XML データを保存して、列のプロパティをリレーショナル列に昇格させることができます。または、再帰しない部分を XML 以外の列に格納し、再帰部分のみを xml データ型の列に格納するためにマッピング テクノロジを使用することができます。

XML テクノロジの選択

ネイティブ XML と XML ビューのどちらの XML テクノロジを選択するかは、主に次の要因によって決まります。

  • ストレージ オプション
    XML データは、ラージ オブジェクトとして保存するのが適切な場合 (製品マニュアルなど) と、リレーショナル列に保存するのに向いている場合 (XML に変換した商品品目など) があります。それぞれのストレージ オプションで保持される忠実性の度合いが異なります。
  • クエリ機能
    クエリの性質およびクエリの対象になる XML データの範囲を基に、最適なストレージ オプションがわかる場合があります。XML ノードの述語評価など、XML データへのきめ細かいクエリは、2 つのストレージ オプションでのサポートの度合いに差があります。
  • XML データのインデックス設定
    XML クエリのパフォーマンスを向上するために、XML データにインデックスを設定できます。インデックス設定のオプションはストレージ オプションによって異なります。ワークロードを最小にするために、適切な選択を行う必要があります。
  • データ変更機能
    一部のワークロードは、XML データのきめ細かい変更を伴います。たとえば、ドキュメント内に新しいセクションを追加する場合などが該当しますが、Web コンテンツなどのその他のワークロードではこのような変更はありません。アプリケーションで、データ変更言語のサポートが重要になる場合があります。
  • スキーマのサポート
    XML データは、スキーマを使用して記述できる場合があります。このときのスキーマは、XML スキーマ ドキュメントであっても、そうでなくてもかまいません。スキーマにバインドされた XML がサポートされるかどうかは、XML テクノロジによって異なります。

どの選択肢を選ぶかで、パフォーマンス特性が異なります。

ネイティブ XML ストレージ

XML データを、サーバーの xml データ型の列に保存できます。次の条件に該当する場合、この方法が適しています。

  • 簡単にサーバーに XML データを保存すると同時に、表示順やドキュメント構造を保持する場合。
  • XML データのスキーマがあるかどうかが明確でない場合。
  • XML データに対し、クエリや変更を行う場合。
  • クエリ処理を高速化するために XML データにインデックスを設定する場合。
  • XML データと XML スキーマを管理するためのシステム カタログ ビューが必要な場合。

構造が多様な XML ドキュメントがある場合、またはリレーショナル構造へのマッピングが難しい複雑なスキーマや複数のスキーマに従った XML ドキュメントがある場合に、ネイティブ XML ストレージが役立ちます。

例 : xml データ型を使用した XML データ モデリング

トピックごとに章が設けられ、それぞれの章の中には複数の節がある構成の XML 形式の製品マニュアルを考えてみます。節には項が含まれる場合があります。したがって、<section> は再帰要素になります。製品マニュアルには、混合コンテンツ、図表、および技術データが大量に含まれているので、データは部分的に構造化された状態です。ユーザーは、「インデックス設定」に関する章の「クラスタ化インデックス」に関する節を検索するなど、関心のあるトピックをコンテキストにより検索したり、技術データにクエリを実行します。

この XML ドキュメントに適したストレージ モデルは xml データ型列です。このモデルであれば、XML データの InfoSet コンテンツが保持されます。XML 列にインデックスを設定して、クエリ パフォーマンスを向上できる利点もあります。

例 : XML データの正確なコピーの保持

たとえば、政府の規定により、XML ドキュメントのテキストの正確なコピーを保持する必要があるとします。署名済み文書、法務文書、株取引の注文書などが該当します。このようなドキュメントは [n]varchar(max) 列に保存できます。

クエリを行うには、実行時にデータを xml データ型に変換して XQuery を実行します。実行時の変換は、ドキュメントが大きい場合は特にコストが高くなる可能性があります。頻繁にクエリを実行する場合は、xml データ型の列にドキュメントを冗長に保存してインデックスを設定しておき、[n]varchar(max) 型の列からドキュメントの正確なコピーを返すことができます。

XML 列は [n]varchar(max) 型の列を基にした計算列にすることができます。ただし、XML 計算列に XML インデックスを作成すること、および [n]varchar(max) 型または varbinary(max) 型の列に XML インデックスを作成することはできません。

XML ビュー テクノロジ

XML スキーマとデータベース内のテーブルとのマッピングを定義することで、永続的なデータの "XML ビュー" を作成します。XML ビューを使用して基になるテーブルのデータを格納する場合に、XML 一括読み込みを行うことができます。XML ビューには XPath Version 1.0 を使用してクエリを実行できます。テーブルでクエリが実行されるときには SQL クエリに変換されます。これと同様に、更新もテーブルに反映されます。

このテクノロジは、次のような場合に役立ちます。

  • 既存のリレーショナル データの XML ビューを使用した XML 中心のプログラミング モデルが必要な場合。
  • 外部のパートナーから提供された XML データのスキーマ (XSD、XDR) がある場合。
  • データの順序が重要ではない場合、クエリ テーブル データが再帰的でない場合、または事前に再帰の最大の深さがわかっている場合。
  • XPath Version 1.0 を使用して、XML ビューからデータに対するクエリや変更を行う場合。
  • XML ビューを使用し、XML データの一括読み込みを行ってそれを基になるテーブルに分解する場合。

例としては、データ交換や Web サービス向けに XML として公開されたリレーショナル データ、固定スキーマにバインドされた XML データなどがあります。詳細については、MSDN オンライン ライブラリ を参照してください。

例 : AXSD (注釈付き XML スキーマ) を使用したデータ モデリング

たとえば、顧客、注文、品目などの既存のリレーショナル データを XML として処理するとします。リレーショナル データに AXSD を使用して、XML ビューを定義します。XML ビューを使用すると、テーブルに XML データを一括で読み込み、XML ビューでリレーショナル データに対するクエリや更新を行うことができます。SQL アプリケーションの実行を中断することなく、XML でマークアップされたデータを他のアプリケーションと交換する必要がある場合に、このモデルが役立ちます。

ハイブリッド モデル

リレーショナル列と xml データ型の列を組み合わせることがデータ モデリングとして適している場合も多くあります。XML データの値の一部をリレーショナル列に保存し、残り (または XML 値全体) を XML 列に保存することができます。そうすることで、リレーショナル列に作成したインデックスやロック特性を制御しやすくなり、パフォーマンスが向上する場合があります。

リレーショナル列に保存する方が適切な値はワークロードによって異なります。たとえば、パス式 /Customer/@CustId を基にすべての XML 値を取得する場合、CustId 属性の値をリレーショナル列に昇格してインデックスを設定することにより、クエリ パフォーマンスが向上する可能性があります。一方で、XML データが冗長性なしで多数のリレーショナル列に分解されている場合、再構成のコストが甚大になる可能性があります。

テーブルのコンテンツを XML に変換した場合など、十分に構造化された XML データでは、すべての値をリレーショナル列にマップすることができ、XML ビュー テクノロジを使用できる場合もあります。

xml データ型を使用したデータ モデリング

ここでは、ネイティブ XML ストレージのデータ モデリングについて説明します。XML データのインデックス設定、プロパティの昇格、および型指定された xml データ型を取り上げます。

同じテーブルと別のテーブル

xml データ型の列は、他にリレーショナル列が存在するテーブルにも作成できます。また、メインのテーブルと外部キー リレーションシップがある別のテーブルにも作成できます。

次の条件のいずれかに該当する場合は、同一のテーブルに xml データ型の列を作成してください。

  • XML 列のデータを取得するが、その列に XML インデックスは不要な場合。
  • xml データ型の列に XML インデックスを作成するときに、メイン テーブルの主キーがクラスタ化キーと同一である場合。詳細については、「xml データ型列のインデックス設定」を参照してください。

次の条件に該当する場合は、別のテーブルに xml データ型の列を作成してください。

  • xml データ型の列に XML インデックスを作成するときに、メイン テーブルの主キーがクラスタ化キーと異なる場合、メイン テーブルに主キーがない場合、またはメイン テーブルがヒープである (クラスタ化キーがない) 場合。メイン テーブルが既に存在する場合、これに該当している可能性があります。
  • テーブルに XML 列が存在することでテーブル スキャンが遅くなるのを避ける場合。テーブル スキャンは、XML が行内に保存されていても行外に保存されていても領域を消費します。

XML データの粒度

XML 列に保存される XML データの粒度は、ロックの際に非常に重要であるだけでなく、更新の際にも重要です。SQL Server では、XML データと XML 以外のデータに対して同一のロック機構を使用します。したがって行レベルのロックを設定すると、行内のすべての XML インスタンスがロックされます。粒度が粗い場合、マルチユーザー シナリオで更新のために大きな XML インスタンスをロックすると、スループットが低下します。一方、分割しすぎるとオブジェクトのカプセル化状態が失われ、再構成のコストが上がります。

優れた設計を行うには、データ モデリングの要件とロックや更新の特性との間でバランスを取ることが重要です。ただし SQL Server 2005 では、実際に保存される XML インスタンスのサイズが決定的な要因になることはありません。

たとえば、新旧の XML インスタンスの比較による BLOB (バイナリ ラージ オブジェクト) やインデックスの部分更新が新しくサポートされるようになったので、それにより XML インスタンスが更新されます。BLOB (バイナリ ラージ オブジェクト) の部分更新は、2 つの XML インスタンスの差異を比較して差分のみを更新します。インデックスの部分更新は、XML インデックスの変更が必要な行のみを変更します。

xml データ型の型指定と制約

SQL Server 2005 の xml データ型には、ISO SQL-2003 標準の xml データ型が実装されています。したがって、型指定されていない XML 列には、整形式の XML Version 1.0 ドキュメントを保存できるほか、テキスト ノードや任意の数の最上位要素が含まれた、いわゆる XML コンテンツ フラグメントを保存することもできます。システムにより、データが整形式であることが確認されます。このとき XML スキーマに列をバインドする必要はなく、広義の整形式でないデータは拒否されます。このことは、型指定されていない XML の変数やパラメータにも該当します。

使用する XML データについて記述した XML スキーマがある場合、スキーマを XML 列に関連付けて型指定された XML を生成できます。XML スキーマを使用して、データを検証すること、クエリのコンパイル時やデータ変更ステートメントによる型の確認を型指定されていない XML に比べ厳密に行うこと、ストレージとクエリの処理を最適化することなどが可能です。

次の条件に該当する場合は、型指定されていない xml データ型を使用してください。

  • XML データに対応するスキーマがない場合。
  • スキーマはあるが、サーバーでデータを検証しない場合。サーバーにデータを保存する前にクライアント側で検証を行う場合、スキーマに従っていない無効な XML データを一時的に保存する場合、サーバーでサポートされていないスキーマ コンポーネント (key、keyref など) を使用する場合などが該当します。

次の条件に該当する場合は、型指定された xml データ型を使用してください。

  • 使用する XML データのスキーマがあり、その XML スキーマに従ってサーバーで XML データを検証する場合。
  • 型情報を基にしたストレージやクエリの最適化を行う場合。
  • クエリをコンパイルするときに型情報を使用する場合。

型指定された XML の列、パラメータ、および変数には、XML ドキュメントまたは XML コンテンツを保存できます。ただし、宣言のときに保存の対象がドキュメントなのかコンテンツなのかをフラグで指定する必要があります。また、XML スキーマのコレクションを指定する必要があります。各 XML インスタンスの最上位要素が 1 つだけの場合、DOCUMENT を指定します。それ以外の場合は CONTENT を指定します。クエリのコンパイルで型を確認するとき、クエリ コンパイラにより DOCUMENT フラグが使用され、シングルトンの最上位要素が推測されます。

XML 列を型指定する以外に、xml データ型の列には型指定されているかどうかを問わず (行または列の) リレーショナル制約を適用できます。制約は、次のような場合に使用してください。

  • ビジネス ルールが XML スキーマでは表現できない場合。たとえば、花屋の配達可能地域が店舗から 80 km 以内に限られている場合などです。このルールは XML 列の制約として記述できます。この制約には、xml データ型のメソッドが必要になる場合があります。
  • 制約が、テーブル内の他の XML 列または XML 以外の列に関係している場合。たとえば、XML インスタンスに現れる Customer の ID (/Customer/@CustId) が、CustomerID リレーショナル列の値と一致する必要がある場合などです。

DTD (文書型定義)

xml データ型の列、変数、およびパラメータは XML スキーマを使用して型指定できますが、DTD では型指定できません。ただしインライン DTD は型指定された XML にも型指定されていない XML にも使用できるので、それを使用して既定値を指定したり、エンティティ参照を展開した形に置き換えることができます。

サード パーティのツールを使用すると、DTD を XML スキーマ ドキュメントに変換できます。変換した XML スキーマはデータベースに読み込むことができます。

xml データ型列のインデックス設定

xml データ型の列には XML インデックスを作成できます。列に保存されている XML インスタンスのすべてのタグ、値、およびパスにインデックスが設定されるので、クエリのパフォーマンスが向上します。次のような場合、XML インデックスの効果が得られる可能性があります。

  • ワークロードで XML 列へのクエリが頻繁に行われる場合。データ変更時の XML インデックスのメンテナンス コストを考慮する必要があります。
  • XML 値が比較的大きいのに対し、取得する部分が比較的小さい場合。インデックスを作成すると、実行時にデータ全体が解析されることを回避し、クエリ処理を効率化するためのインデックス参照を利用できます。

XML 列の最初のインデックスをプライマリ XML インデックスといいます。プライマリ XML インデックスを使用するときは、一般的なクエリを高速化するために、その XML 列に 3 種類のセカンダリ XML インデックスを作成できます。詳細については、次のセクションを参照してください。

プライマリ XML インデックス

プライマリ XML インデックスにより、XML 列に保存されている XML インスタンスのすべてのタグ、値、およびパスにインデックスが設定されます。ベース テーブル (XML 列があるテーブル) には、主キーにクラスタ化インデックスが必要です。主キーはインデックス行とベース テーブルの行を関連付けるために使用されます。XML 列からは完全な XML インスタンスが取得されます (たとえば SELECT * など)。クエリを実行するときはプライマリ XML インデックスが使用され、インデックス自体によってスカラ値または XML サブツリーが返されます。

例 : プライマリ XML インデックスの作成

ここからはほとんどの例で、型指定されていない XML 列を含んだテーブル T (pk INT PRIMARY KEY, xCol XML) を使用します。XML 列は、型指定された XML に簡単に拡張できます。型指定された XML の使用方法の詳細については、「xml データ型」を参照してください。説明を簡単にするため、次に示す XML データ インスタンスに対するクエリについて説明します。

<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
   <title>Writing Secure Code</title>
   <author>
      <first-name>Michael</first-name>
      <last-name>Howard</last-name>
   </author>
   <author>
      <first-name>David</first-name>
      <last-name>LeBlanc</last-name>
   </author>
   <price>39.99</price>
</book>

次のステートメントを実行すると、テーブル T の XML 列 xCol に idx_xCol という XML インデックスが作成されます。

CREATE PRIMARY XML INDEX idx_xCol on T (xCol)

セカンダリ XML インデックス

プライマリ XML インデックスを作成した後で、ワークロードの各種クエリを高速化するためにセカンダリ XML インデックスを作成できます。セカンダリ XML インデックスには PATH、PROPERTY、および VALUE の 3 種類があり、それぞれパスに基づくクエリ、カスタム プロパティ管理のシナリオ、値に基づくクエリに効果があります。PATH インデックスは、列にあるすべての XML インスタンスに対し、ドキュメントの順に各 XML ノードのパスと値の組み合わせで B+ ツリーを構築します。PROPERTY インデックスは、各 XML インスタンス内の PK (ベース テーブルの主キー)、パス、値の組み合わせで、クラスタ化した B+ ツリーを構築します。最後に VALUE インデックスは、XML 列にあるすべての XML インスタンスに対し、ドキュメントの順に各ノードの値とパスの組み合わせで B+ ツリーを構築します。

次に、上記のインデックスを 1 種類以上作成する場合のガイドラインを示します。

  • ワークロードで XML 列にパス式が多用されている場合、PATH セカンダリ XML インデックスを作成するとワークロードを高速に処理できることがあります。一般的な例では、Transact-SQL の WHERE 句で XML 列に対し exist() メソッドが使用される場合があります。
  • パス式を使用して個々の XML インスタンスから複数の値を取得する場合、PROPERTY インデックスで各 XML インスタンス内のパスをクラスタ化すると効果が得られる可能性があります。このシナリオは、主キーの値がわかっていてオブジェクトのプロパティをフェッチするプロパティ バッグ シナリオで主に発生します。
  • XML インスタンスの値を、要素名または属性名がわからないままクエリで取得する場合、VALUE インデックスを作成できます。//author[last-name="Howard"] (<author> 要素が階層のどのレベルにあってもよい) のように、descendant 軸を参照する場合がその典型です。また、ワイルドカードを使用するクエリ (いずれかの属性に値 "novel" が指定された <book> 要素をクエリで検索する /book [@* = "novel"] など) もこれに該当します。
例 : パスに基づく参照

たとえば、ワークロードで次のクエリが頻繁に行われるとします。

SELECT pk, xCol
FROM   T
WHERE  xCol.exist ('/book/@genre[.="novel"]') = 1

パス式 /book/@genre および値 "novel" は PATH インデックスのキー フィールドに対応します。したがって、このワークロードには PATH セカンダリ XML インデックスが有効です。

CREATE XML INDEX idx_xCol_Path on T (xCol)
   USING XML INDEX idx_xCol FOR PATH
例 : オブジェクトのプロパティのフェッチ

テーブル T の各行から書籍のプロパティ genre、title、および ISBN を取得する次のクエリを考えてみます。

SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
    xCol.value ('(/book/title/text())[1]', 'varchar(50)'),
    xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM    T

この例では PROPERTY インデックスが有用なので、次のように作成します。

CREATE XML INDEX idx_xCol_Property on T (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY
例 : 値に基づくクエリ

次のクエリは descendant-or-self 軸 (//) で部分的なパスを指定しています。これにより、ISBN の値を基にした参照に VALUE インデックスを役立てることができます。

SELECT xCol
FROM    T
WHERE    xCol.exist ('//book/@ISBN[. = "0-7356-1588-2"]') = 1

VALUE インデックスは次のように作成します。

CREATE XML INDEX idx_xCol_Value on T (xCol)
   USING XML INDEX idx_xCol FOR VALUE

XML 列のフルテキスト インデックス

XML 列にフルテキスト インデックスを作成して XML 値のコンテンツにインデックスを設定できますが、XML マークアップは無視されます。属性値はマークアップの一部と見なされるのでフルテキスト インデックスには組み込まれず、要素タグはトークンの境界として使用されます。可能であれば次のようにしてフルテキスト検索と XML インデックスを組み合わせることができます。

  • まず、SQL フルテキスト検索を使用して、対象の XML 値をフィルタ処理します。
  • 次に、XML 列の XML インデックスを使用する XML 値にクエリを実行します。
例 : フルテキスト検索と XML クエリの組み合わせ

XML 列でフルテキスト インデックスを作成した後、XML 値が書名に語 "custom" を含んでいることを次のクエリで確認します。

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'custom') 
AND    xCol.exist('/book/title/text()[contains(.,"custom")]') =1

contains() メソッドによって、フルテキスト インデックスを使用してドキュメントのどこかに語 "custom" を含んでいる XML 値をサブセットとして取り出します。exist() 句により、書名に語 "custom" が使用されていることを確認します。

contains() を使用するフルテキスト検索と XQuery contains() は意味が異なります。前者はステミングによるトークンの照合で、後者は部分文字列の照合です。したがって、書名に "run" が使用されている文字列を検索する場合、フルテキストの contains() と XQuery の contains() をいずれも満たす "run"、"runs"、および "running" が検出されます。しかし、この例のクエリでは語 "customizable" が書名に使用されていても、XQuery の contains() は満たしますがフルテキストの contains() で失敗するので検出されません。一般に純粋な部分文字列の検索を行うには、フルテキストの contains() 句を削除する必要があります。

さらに、フルテキスト検索ではステミングが使用されますが、XQuery の contains() はリテラルの照合です。両者の違いを次の例で示します。

例 : ステミングを使用した XML 値のフルテキスト検索

上記の例で行った XQuery contains() による確認は、通常は省略できません。次のクエリについて考えてみます。

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'run') 

ドキュメント内で語 "ran" が使用されていると、ステミングによりこの検索条件に一致します。XQuery では検索コンテキストは確認されません。

フルテキスト インデックスが作成されたリレーショナル列に AXSD を使用して XML を分解した場合、XML ビューに XPath クエリを実行しても基になるテーブルのフルテキスト検索は行われません。

プロパティの昇格

クエリが主に少数の要素や属性の値に対して行われる場合、対象になる値をリレーショナル列に昇格できます。XML インスタンス全体を取得する一方で、XML データの一部に対してクエリを実行する場合に昇格が役立ちます。XML 列に XML インデックスを作成する必要はありません。代わりに、昇格した列にインデックスを設定できます。クエリは昇格した列を使用するように記述する必要があります。クエリ オプティマイザは、クエリの対象を XML 列から、昇格した列に振り替えないためです。

昇格した列は、同一のテーブルで計算列にすることができます。また、任意のテーブルでユーザーが管理する独立した列にすることもできます。これは、各 XML インスタンスから単一の値を昇格するときには十分です。しかし、複数の値から構成されるプロパティの場合、個々のプロパティ用に個別のテーブルを作成する必要があります。詳細については、次のセクションを参照してください。

xml データ型を基にした計算列

xml データ型のメソッドを呼び出すユーザー定義関数を使用して、計算列を作成できます。計算列の型は、XML を含めどの SQL 型でもかまいません。これを次の例に示します。

例 : xml データ型のメソッドを基にした計算列

書籍の ISBN 番号を取得するユーザー定義関数を作成します。

CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
   DECLARE @ISBN   varchar(20)
   SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
   RETURN @ISBN 
END

ISBN を保存する計算列をテーブルに追加します。

ALTER TABLE      T
ADD   ISBN AS dbo.udf_get_book_ISBN(xCol)

計算列は、通常の方法でインデックスを設定できます。

例 : xml データ型のメソッドを基にした計算列へのクエリ

ISBN が 0-7356-1588-2 の <book> を取得します。

SELECT xCol
FROM   T
WHERE  xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1

XML 列へのクエリを次のように書き換えると、計算列を使用できます。

SELECT xCol
FROM   T
WHERE  ISBN = '0-7356-1588-2'

xml データ型を返すユーザー定義関数を作成し、その関数で計算列を作成できます。ただし、XML 計算列には XML インデックスを作成できません。

プロパティ テーブルの作成

XML データの中から複数の値で構成されるプロパティの一部を 1 つ以上のテーブルに昇格させ、インデックスを作成してクエリの対象をそのテーブルに振り替えることができます。クエリ ワークロードの大半が少数のプロパティで占められているシナリオが典型的です。次の操作を実行できます。

  • 複数の値から構成されるプロパティを格納するためのテーブルを 1 つ以上作成します。1 つのプロパティを 1 つのテーブルに保存し、プロパティ テーブルでベース テーブルの主キーを複製すると、ベース テーブルとの逆結合に便利です。
  • プロパティの相対順序を保持する場合、相対順序を保持するための列を別に設ける必要があります。
  • プロパティ テーブルを管理するためのトリガを XML 列に作成します。トリガ内では次のいずれかを行うことができます。
    • nodes()value() などの xml データ型のメソッドを使用して、プロパティ テーブルの行を挿入および削除します。
    • CLR (共通言語ランタイム) でストリーミング テーブル値関数を作成し、プロパティ テーブルの行を挿入および削除します。
    • 主キーを使用してテーブルどうしを結合し、プロパティ テーブルに SQL アクセスを行うクエリ、およびベース テーブルの XML 列に XML アクセスを行うクエリを記述します。
例 : プロパティ テーブルの作成

たとえば、著者の名 (ファースト ネーム) を昇格させるとします。共著の場合もあるので、名は複数の値から構成されるプロパティです。それぞれの名は、プロパティ テーブルの個別の行に保存されます。逆結合のため、ベース テーブルの主キーをプロパティ テーブルで複製します。

create table tblPropAuthor (propPK int, propAuthor varchar(max))
例 : XML インスタンスから行セットを生成するユーザー定義関数の作成

次のテーブル値関数 udf_XML2Table は、主キーの値と XML インスタンスを受け取ります。<book> 要素のすべての著者の名を取得し、主キーと名の組み合わせから構成される行セットを返します。

create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
      select @pk, nref.value('.', 'varchar(max)')
      from   @xCol.nodes('/book/author/first-name') R(nref)
      return
end
例 : プロパティ テーブルにデータを格納するトリガの作成

次の挿入トリガを使用して、プロパティ テーブルに行を挿入します。

create trigger trg_docs_INS on T for insert
as
      declare @wantedXML xml
      declare @FK int
      select @wantedXML = xCol from inserted
      select @FK = PK from inserted

   insert into tblPropAuthor
   select * from dbo.udf_XML2Table(@FK, @wantedXML)

次の削除トリガを使用して、削除する行の主キーの値を基に、プロパティ テーブルから行を削除します。

create trigger trg_docs_DEL on T for delete
as
   declare @FK int
   select @FK = PK from deleted
   delete tblPropAuthor where propPK = @FK

次の更新トリガを使用して、更新する XML インスタンスに対応するプロパティ テーブルの既存の行を削除し、新しい行を挿入します。

create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
      declare @FK int
      declare @wantedXML xml
      select @FK = PK from deleted
      delete tblPropAuthor where propPK = @FK

   select @wantedXML = xCol from inserted
   select @FK = pk from inserted

   insert into tblPropAuthor 
      select * from dbo.udf_XML2Table(@FK, @wantedXML)
end
例 : 著者が "David" の XML インスタンスの検索

XML 列に対するクエリも作成できますが、プロパティ テーブルで名 "David" を検索し、ベース テーブルとの逆結合を実行して XML インスタンスを返すこともできます。次に例を示します。

SELECT xCol 
FROM     T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE    tblPropAuthor.propAuthor = 'David'
例 : CLR ストリーミング テーブル値関数を使用したソリューション

このソリューションは、次の手順で実行します。

  1. CLR クラス SqlReaderBase を定義します。このクラスは ISqlReader を実装し、XML インスタンスにパス式を適用することでストリーミング テーブル値出力を生成します。
  2. CLR クラスを起動するため、アセンブリおよび Transact-SQL ユーザー定義関数を作成します。
  3. ユーザー定義関数を使用して、プロパティ テーブルのメンテナンスに使用する挿入トリガ、更新トリガ、および削除トリガを定義します。

まず、ストリーミング CLR 関数を作成します。xml データ型は ADO.NET のマネージ クラス SqlXml として公開します。このデータ型は XmlReader を返す CreateReader() メソッドをサポートします。

ms187508.note(ja-jp,SQL.90).gifメモ :
このセクションの例のコードでは、XPathDocument および XPathNavigator を使用しています。この 2 つはすべての XML ドキュメントをメモリに読み込みます。大きな XML ドキュメントを処理するためにこのサンプルと同様のコードを使用する場合、このコードにはスケーラビリティはありません。代わりに、メモリの割り当てを少なく抑え、可能な限りストリーミング インターフェイスを使用してください。パフォーマンスの詳細については、「CLR 統合のアーキテクチャ」を参照してください。
public class c_streaming_xml_tvf {
   public static ISqlReader streaming_xml_tvf 
(SqlXml xmlDoc, string pathExpression) {
      return (new TestSqlReaderBase (xmlDoc, pathExpression));
   }
}

// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;         
   public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;      

   public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {   
      // Variables for XPath navigation
      XPathDocument xDoc;
      XPathNavigator xNav;
      XPathExpression xPath;
   
      // Set sql metadata
      m_rgSqlMetaData = new SqlMetaData[1];
      m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",  
SqlDbType.NVarChar,50);   
   
      //Set up the Navigator
      if (!xmlDoc.IsNull)
          xDoc = new XPathDocument (xmlDoc.CreateReader());
      else
          xDoc = new XPathDocument ();
      xNav = xDoc.CreateNavigator();
      xPath = xNav.Compile (pathExpression);
      m_iterator = xNav.Select(xPath);
   }
   public bool Read() {
      bool moreRows = true;
      if (moreRows = m_iterator.MoveNext())
         FirstName = new SqlChars (m_iterator.Current.Value);
      return moreRows;
   }
}

次に、アセンブリ、および CLR 関数 streaming_xml_tvf に対応する Transact-SQL ユーザー定義関数 SQL_streaming_xml_tvf (ここには示していません) を作成します。このユーザー定義関数を使用して、行セットを生成するためのテーブル値関数 CLR_udf_XML2Table を定義します。

create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
   select @pk, FirstName 
   FROM   SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
      return
end

最後に、「プロパティ テーブルにデータを格納するトリガの作成」で示したトリガを、udf_XML2Table 関数を CLR_udf_XML2Table 関数に置き換えて定義します。挿入トリガは次の例のようになります。

create trigger CLR_trg_docs_INS on T for insert
as
   declare @wantedXML xml
   declare @FK int
   select @wantedXML = xCol from inserted
   select @FK = PK from inserted

   insert into tblPropAuthor
      select *
   from    dbo.CLR_udf_XML2Table(@FK, @wantedXML)

削除トリガは CLR を使用しない場合と同じです。更新トリガは関数 udf_XML2Table() を CLR_udf_XML2Table() に置き換えます。

XML スキーマ コレクション

XML スキーマ コレクションは、リレーショナル スキーマによりスコープが設定されたメタデータ エンティティです。コレクションには、関連のある XML スキーマ (<xs:import> など)、または関連のない XML スキーマが 1 つ以上含まれます。XML スキーマ コレクション内の各 XML スキーマは、対象になる名前空間を使用して識別されます。

XML スキーマ コレクションは、CREATE XML SCHEMA COLLECTION (Transact-SQL) 構文で 1 つ以上の XML スキーマを指定して作成します。既存の XML スキーマに XML スキーマ コンポーネントを追加したり、XML スキーマ コレクションにスキーマを追加するには、ALTER XML SCHEMA COLLECTION (Transact-SQL) 構文を使用します。XML スキーマ コレクションは、SQL Server 2005 のセキュリティ モデルによって SQL オブジェクトと同様にセキュリティで保護できます。

複数の型指定が行われている列

XML スキーマ コレクション C では、複数の XML スキーマによって XML 列 xCol の型指定を行います。また、DOCUMENT フラグおよび CONTENT フラグにより、それぞれ XML ツリーまたは XML フラグメントのどちらを列 xCol に保存できるかを指定します。

DOCUMENT を指定した各 XML インスタンスでは、その最上位要素の対象になる名前空間が指定され、その名前空間に従って型指定と検証が行われます。一方 CONTENT の場合、C に含まれる対象になる名前空間のうち任意の 1 つをそれぞれの最上位要素に指定できます。XML インスタンスの検証と型指定は、インスタンス内で指定されたすべての対象になる名前空間に従って行われます。

スキーマの展開

XML スキーマ コレクションは、XML の列、変数、およびパラメータの型指定に使用します。また、XML スキーマの展開のためのメカニズムを実現します。たとえば、対象になる名前空間が BOOK-V1 の XML スキーマを XML スキーマ コレクション C に追加するとします。C で型指定された XML 列 xCol には、BOOK-V1 スキーマに従った XML データを保存できます。

次に、複合型の定義、最上位要素の宣言など、新しいスキーマ コンポーネントで XML スキーマを拡張するとします。新しいスキーマ コンポーネントは BOOK-V1 スキーマに追加できます。その際、列 xCol の既存の XML データを再検証する必要はありません。

後で新しい XML スキーマが提供され、対象になる名前空間 BOOK-V2 が選択されるとします。この XML スキーマは C に追加できます。XML 列に BOOK-V1 と BOOK-V2 のインスタンスを保存し、どちらの名前空間に従う XML インスタンスに対してもクエリやデータ変更を実行できます。

XML データの読み込み

SQL Server 2000 から SQL Server 2005 への XML データの転送

SQL Server 2005 では、いくつかの方法で XML データを転送できます。次に例を示します。

  • SQL Server 2000 データベースの [n]text 型または image 型の列にデータが含まれている場合、SQL Server 2005 Integration Services (SSIS) を使用して SQL Server 2005 データベースにテーブルをインポートできます。ALTER TABLE ステートメントを使用して列の型を XML に変更します。
  • bcp out を使用して SQL Server 2000 からデータの一括コピーを実行し、bcp in を使用して SQL Server 2005 データベースにそのデータの一括挿入を実行できます。
  • SQL Server 2000 データベースのリレーショナル列にデータが含まれている場合、新しいテーブルを作成し、[n]text 型の列や行 ID を保存する主キー列 (任意) を含めます。クライアント側プログラミングを使用して、FOR XML によりサーバーで生成された XML を取得し、[n]text 型の列に書き込みます。その後で、既に説明した技法により SQL Server 2005 データベースにデータを転送します。XML を SQL Server 2005 データベースの XML 列に直接書き込むこともできます。
例 : XML への列の型の変更

テーブル R の [n]text 型または image 型の列 XYZ の型を、型指定されていない XML に変更するとします。型を変更するには次のステートメントを実行します。

ALTER TABLE R ALTER COLUMN XYZ XML
  • 必要であれば XML スキーマ コレクションを指定して、型指定された XML にすることができます。

XML データの一括読み込み

bcp など、SQL Server の一括読み込み機能によって XML データをサーバーに一括で読み込むことができます。OPENROWSET を使用すると、ファイルから XML 列にデータを読み込むことができます。この点について、次に例で説明します。

例 : ファイルからの XML の読み込み

この例では、テーブル T に行を挿入する方法を示します。C:\MyFile\xmlfile.xml から CLOB として XML 列の値を読み込み、整数型の列に値 10 を保存します。

INSERT INTO T
SELECT 10, xCol
FROM    (SELECT *    
    FROM OPENROWSET (BULK 'C:\MyFile\xmlfile.xml', SINGLE_CLOB) 
 AS xCol) AS R(xCol)

テキスト エンコード

SQL Server 2005 では XML データを Unicode (UTF-16) で保存します。サーバーから取得する XML データは UTF-16 エンコードで出力されます。それ以外のエンコードが必要な場合、取得したデータに必要な変換を行う必要があります。変換した XML データのエンコードが異なる場合があります。その場合は、注意してデータを読み込む必要があります。次に例を示します。

  • Unicode (UCS-2、UTF-16) のテキスト XML は、問題なく XML 列、変数、またはパラメータに代入できます。
  • 基になるコード ページの都合で Unicode ではなく、かつ暗黙のエンコードの場合、データベースの文字列のコード ページは読み込むコード ポイントと同一か、互換性がある必要があります。必要であれば COLLATE を使用します。サーバーにそのようなコード ページが存在しない場合、エンコードを修正する明示的な XML 宣言を追加する必要があります。
  • 明示的なエンコードを使用するには、コード ページに連動しない varbinary() 型を使用するか、適切なコード ページの文字列型を使用します。次に、データを XML の列、変数、またはパラメータに割り当てます。
例 : エンコードの明示的な指定

明示的な XML 宣言が行われていない XML ドキュメント vcdoc を varchar(max) として保存しているとします。次のステートメントは、エンコード "iso8859-1" を指定した XML 宣言を追加し、この XML ドキュメントを宣言に連結します。バイト表現を保持するために結果を varbinary(max) にキャストし、最終的にその結果を XML にキャストします。その結果、XML プロセッサで、指定したエンコード "iso8859-1" に従ってデータを解析し、対応する文字列値の UTF-16 表現を生成できます。

SELECT CAST( 
CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc) AS VARBINARY (MAX)) 
 AS XML)

XQuery と型の推定

Transact-SQL に埋め込む XQuery は、xml データ型へのクエリでサポートされる言語です。この言語は、マイクロソフトやすべての主要データベース製品ベンダが加盟する W3C (World Wide Web Consortium) により開発中です。XQuery はナビゲーション言語として XPath Version 2.0 を含んでいます。xml データ型に対し、データを変更するための言語構造を使用できます。SQL Server でサポートされている XQuery の構造、関数、および演算子の詳細については、「XML データ型に対する XQuery 関数」を参照してください。

エラー モデル

XQuery 式や XML DML ステートメントの構文が誤っている場合は、コンパイル エラーが返されます。コンパイル フェーズでは、XQuery 式や DML ステートメントの静的な型が正しいことが確認され、XML スキーマを使用して型指定された XML にするための型の推定が行われます。型の安全性違反により実行時に式が失敗する可能性がある場合は、静的な型エラーが発生します。静的なエラーの例には、文字列への整数の追加、型指定されたデータが存在しないノードへのクエリの実行などがあります。

W3C 標準からは外れますが、XQuery の実行時エラーは空のシーケンスに変換されます。この空のシーケンスは、呼び出し時の状況に応じ、空の XML または NULL としてクエリの結果に反映される場合があります。

実行時のキャスト エラーは空のシーケンスに変換されますが、明示的に正しい型にキャストすることで静的なエラーを回避できます。

次のセクションで、型の確認について詳しく説明します。

シングルトンの確認

実行時にシングルトンであることが確実かどうかをコンパイラで判断できない場合、シングルトンを必要とするロケーション ステップ、関数パラメータ、および演算子はエラーを返します。型指定されていないデータではこの問題が頻繁に発生します。たとえば、属性の参照には単一の親要素が必要ですが、単一の親ノードを選択する序数があれば問題を回避できます。node()value() の組み合わせを評価して属性値を抽出するときは、序数を指定する必要がない場合もあります。次の例を参照してください。

例 : 既知のシングルトン

次の例で、nodes() メソッドは <book> 要素ごとに個別の行を生成します。value() メソッドは <book> ノードで評価され、@genre の値を抽出して属性にします。この値はシングルトンです。

SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   T CROSS APPLY xCol.nodes('//book') AS R(nref)

型指定された XML の型の確認には XML スキーマが使用されます。XML スキーマでノードがシングルトンとして指定されている場合、その情報がコンパイラで使用され、エラーは発生しません。その指定がない場合は、単一のノードを選択する序数が必要です。特に、/book//title のように descendant-or-self 軸 (//) を使用すると、XML スキーマでシングルトンが指定されていても <title> 要素のシングルトンの基数の推定が厳密ではなくなります。したがって、この部分は「(/book//title)[1]」と書き換えます。

型の確認のときは、//first-name[1] と (//first-name)[1] の違いを常に認識することが重要です。前者は兄弟の中で最も左の <first-name> ノードのみから構成される <first-name> ノードのシーケンスを返します。後者は XML インスタンスのドキュメント順で最初のシングルトンの <first-name> ノードを返します。

例 : value() の使用

型指定されていない XML 列に対し次のクエリを実行すると、静的なコンパイル エラーが発生します。value() の最初の引数はシングルトン ノードでなければなりませんが、<last-name> ノードが実行時に現れる回数が 1 回のみかどうかをコンパイラで判断できないためです。

SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM   T

次のような解決策が考えられます。

SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM   T

しかし、各 XML インスタンスで <author> ノードが複数回現れる場合があるので、これではエラーは解決しません。次のように書き換えると、正常に動作します。

SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T

このクエリは、各 XML インスタンスの最初の <last-name> 要素の値を返します。

parent 軸

ノードの型を判別できない場合、anyType 型になります。この型は他の型に暗黙的にキャストされることはありません。anyType は、xCol.query('/book/@genre/../price') など、parent 軸を使用したナビゲーションで特に発生します。親ノードの型が anyType であると判別されます。要素も XML スキーマで anyType として定義される場合があります。いずれの場合も、正確な型情報が失われて静的な型エラーが発生することが多いので、アトミック値を明示的に特定の型にキャストしておく必要があります。

Data()、text()、および string() の各アクセサ

XQuery には、型指定されたスカラ値をノードから抽出する関数 fn:data()、テキスト ノードを返すノード テスト text()、およびノードの文字列値を返す関数 fn:string() が用意されています。この 3 つのアクセサの使用方法が紛らわしい場合があります。次に、SQL Server 2005 でこれらを使用する場合のガイドラインを示します。説明のため、XML インスタンス <age>12</age> を使用します。

  • 型指定されていない XML : パス式 /age/text() はテキスト ノード "12" を返します。関数 fn:data(/age) および fn:string(/age) は文字列値 "12" を返します。
  • 型指定された XML : 式 /age/text() は型指定された単純型の <age> 要素に対する静的なエラーを返します。一方、fn:data(/age) は整数 12 を返します。fn:string(/age) は文字列 "12" を返します。

union 型に使用する関数と演算子

union 型は型の確認があるので注意して処理する必要があります。ここでは、例を使って 2 つの問題を説明します。

例 : union 型に使用する関数

union 型の <r> 要素の定義を考えてみます。

<xs:element name="r">
<xs:simpleType>
   <xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>

XQuery では、"平均" 関数 fn:avg (//r) が引数として受け取る <r> 要素の型 (xs:int、xs:float、xs:double) が異なる場合、XQuery コンパイラが値を加算できないので、この関数は静的なエラーを返します。これを解決するには、関数の呼び出し部分を「fn:avg(for $r in //r return $r cast as xs:double ?)」と書き換えます。

例 : union 型に使用する演算子

加算演算子 ('+') を使用するにはオペランドの正確な型が必要です。したがって、式 (//r)[1] + 1 は既に説明した要素 <r> の型定義に関するエラーを返します。解決策の 1 つは、「(//r)[1] cast as xs:int? +1」 ("?" は 0 回または 1 回の出現を示す) に書き換える方法です。SQL Server 2005 では、すべてのキャストは実行時エラーの結果として空のシーケンスになる可能性があるので、"cast as" に "?" を付ける必要があります。

Value()、Nodes()、および OpenXML()

SELECT 句では xml データ型に value() メソッドを複数回使用して、抽出した値の行セットを生成できます。nodes() メソッドは、追加のクエリに使用するために選択した各ノードの内部参照を生成します。nodes() メソッドと value() メソッドを併用すると、行セットに複数の列があるとき、および行セット生成のためのパス式が複雑なときに、効率的に行セットを生成できる場合があります。

nodes() メソッドによって特別な xml データ型のインスタンスが生成され、それぞれのインスタンスのコンテキストは選択された個別のノードに設定されます。このような XML インスタンスは、query() メソッド、value() メソッド、nodes() メソッド、および exist() メソッドをサポートし、count(*) 集計で使用できます。それ以外の使用方法ではエラーが発生します。

例 : nodes() の使用

名が "David" ではない著者の姓と名を抽出するとします。2 つの列 FirstName および LastName から構成される行セットとしてこの情報を抽出します。そのためには次に示すように、nodes() メソッドと value() メソッドを使用します。

SELECT nref.value('(first-name/text())[1]', 'nvarchar(50)') FirstName,
       nref.value('(last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('first-name[. != "David"]') = 1

この例では、nodes('//author') により各 XML インスタンスの <author> 要素への参照が格納された行セットが生成されます。その参照について value() メソッドを評価することで、著者の姓および名を取得します。

SQL Server 2000 には、OpenXml() を使用して XML インスタンスから行セットを生成する機能があります。行セットにリレーショナル スキーマを指定し、その中の列に XML インスタンス内の値をどのようにマップするかを指定できます。

例 : xml データ型での OpenXml() の使用

上記の例のクエリは、OpenXml() を使用して次のように書き換えることができます。そのためには、各 XML インスタンスを読み取って XML 変数に代入し、OpenXML に渡すカーソルを作成します。

DECLARE name_cursor CURSOR
FOR
   SELECT xCol 
   FROM   T
OPEN name_cursor
DECLARE @xmlVal XML
DECLARE @idoc int
FETCH NEXT FROM name_cursor INTO @xmlVal

WHILE (@@FETCH_STATUS = 0)
BEGIN
   EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal
   SELECT   *
   FROM   OPENXML (@idoc, '//author')
          WITH (FirstName  varchar(50) 'first-name',
                LastName   varchar(50) 'last-name') R
   WHERE  R.FirstName != 'David'

   EXEC sp_xml_removedocument @idoc
   FETCH NEXT FROM name_cursor INTO @xmlVal
END
CLOSE name_cursor
DEALLOCATE name_cursor 

OpenXml() によりメモリ内表現が作成され、クエリ プロセッサの代わりに作業テーブルが使用されます。この関数は XQuery エンジンではなく MSXML Version 3.0 の XPath Version 1.0 プロセッサを使用します。同一の XML インスタンスであっても、OpenXml() の複数の呼び出しで作業テーブルを共有することはありません。このため、スケーラビリティが制限されます。OpenXml() を使用すると、WITH 句を指定しない場合に XML データのエッジ テーブル形式にアクセスできます。また、別の "オーバーフロー" 列内の残りの XML 値を使用できます。

nodes() 関数と value() 関数を組み合わせると、XML インデックスを効果的に使用できます。つまり、OpenXml よりもスケーラビリティに優れています。

FOR XML を使用した行セットからの XML の生成

新しい TYPE ディレクティブを指定した FOR XML を使用することで、xml データ型のインスタンスを行セットから生成できます。

結果は xml データ型の列、変数、またはパラメータに代入できます。また、FOR XML を入れ子にして階層構造を作成することもできます。入れ子にした FOR XML は FOR XML EXPLICIT よりも記述が容易ですが、階層が深いとパフォーマンスが低下する場合があります。FOR XML には新しい PATH モードも導入されています。この新しいモードで、列の値が現れる XML ツリーのパスを指定します。

新しい FOR XML TYPE ディレクティブを使用すると、リレーショナル データの読み取り専用の XML ビューを SQL 構文で定義できます。このビューには、次の例で示すように SQL ステートメントおよびそれに埋め込まれた XQuery を使用してクエリを実行できます。この SQL ビューをストアド プロシージャで参照することもできます。

例 : 生成された xml データ型を返す SQL ビュー

次の SQL ビュー定義により、リレーショナル列 pk および XML 列から取得した著者氏名の XML ビューが作成されます。

CREATE VIEW V (xmlVal) AS
SELECT pk, xCol.query('/book/author')
FROM   T
FOR XML AUTO, TYPE

V ビューには、1 列の XML 型の列 xmlVal から構成される 1 行が含まれ、通常の xml データ型のインスタンスと同様にクエリを実行できます。たとえば、次のクエリは名が "David" の著者を返します。

SELECT xmlVal.query('//author[first-name = "David"]')
FROM   V

SQL ビュー定義は、注釈付きスキーマを使用して作成する XML ビューと似ている部分があります。しかし、両者には重大な違いがあります。SQL ビュー定義は読み取り専用であり、埋め込みの XQuery で操作する必要があります。XML ビューは、注釈付きスキーマを使用して作成します。また、SQL ビューが XQuery 式を適用する前に XML の結果を具体化するのに対し、XML ビューへの XPath クエリは基になるテーブルで SQL クエリを評価します。

ビジネス ロジックの追加

XML データにはいくつかの方法でビジネス ロジックを追加できます。

  • XML データの挿入や変更のときに領域固有の制約を強制する行制約または列制約を記述できます。
  • 列の値を挿入または更新したときに起動するトリガを XML 列に記述できます。トリガには領域固有の検証規則を含めたり、トリガを使用してプロパティ テーブルにデータを格納することができます。
  • XML 値を渡す SQLCLR 関数をマネージ コードに記述し、System.Xml 名前空間の提供する XML 処理機能を使用できます。たとえば、XML データに XSL 変換を適用する場合に使用します。または、XML のシリアル化を解除して 1 つ以上のマネージ クラスにし、マネージ コードで操作することができます。
  • ビジネス ニーズに合わせて XML 列の処理を開始する Transact-SQL ストアド プロシージャや関数を記述できます。
例 : XSL 変換の適用

xml データ型のインスタンスおよびファイルに保存されている XSL 変換を受け取り、XML データに変換を適用し、変換後の XML を結果として返す CLR 関数 TransformXml() を考えてみます。次に示すのは、C# で記述した関数の骨組みです。

public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslCompiledTransform xform = new XslCompiledTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load(xslDoc);

   // Load XML data 
   XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());

   // Return the transformed value
   MemoryStream xsltResult = new MemoryStream();
   xform.Transform(xDoc, null, xsltResult);
   SqlXml retSqlXml = new SqlXml(xsltResult);
   return (retSqlXml);
} 

アセンブリを登録し、TransformXml() に対応する Transact-SQL のユーザー定義関数 SqlXslTransform() を作成すると、次のクエリに示すように Transact-SQL からその関数を呼び出すことができます。

SELECT SqlXslTransform (xCol, 'C:\MyFile\xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

クエリの結果には、変換後の XML の行セットが含まれます。

SQLCLR によって、XML データをテーブルに分解するかプロパティを昇格させてから、System.Xml 名前空間のマネージ クラスを使用して XML データにクエリを実行できるようになります。詳細については、SQL Server Books Online および .NET Framework SDK のドキュメントを参照してください。

複数の領域にまたがるクエリ

データがリレーショナル列と xml データ型の列の両方に存在している場合、リレーショナル データと XML データの処理を組み合わせたクエリを記述できます。たとえば、リレーショナル列と XML 列のデータを FOR XML で xml データ型のインスタンスに変換し、XQuery でクエリを実行できます。それとは逆に、XML 値から行セットを生成し、Transact-SQL でクエリを実行することもできます。

複数の領域にまたがるクエリを効率的に使用する方法の 1 つは、XQuery 式または XML DML 式で SQL 変数または SQL 列の値を使用することです。

  • sql:variable() を使用して、SQL 変数の値を XQuery 式または XML DML 式で使用できます。
  • sql:column() を使用して、リレーショナル列の値を XQuery 式または XML DML 式で使用できます。

この 2 つの方式により、次の例で示すようにクエリをパラメータ化できます。ただし、sql:variable() および sql:column() では XML 型とユーザー定義型を使用できません。

例 : sql:variable() を使用した複数の領域にまたがるクエリ

次のクエリは、「例 : xml データ型のメソッドを基にした計算列へのクエリ」を変更したものです。変更後のクエリでは、前のクエリで使用した特定の ISBN を渡すために SQL 変数 @isbn を使用しています。定数を sql:variable() に置き換えたことにより、ISBN が 0-7356-1588-2 の書籍だけでなく、どの ISBN でも検索できます。

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
SELECT  xCol
FROM    T
WHERE   xCol.exist ('/book/@ISBN[. = sql:variable("@isbn")]') = 1

sql:column() も同様に使用できますが、より多くの利点があります。コストベースのクエリ オプティマイザの判断により、効率を上げるために列のインデックスが使用される場合があります。また、昇格したプロパティが計算列に保存される場合があります。

ネイティブ XML サポートのカタログ ビュー

カタログ ビューは XML の使用方法についてのメタデータ情報を提供します。次のセクションで、一部のビューについて説明します。

XML インデックス

XML インデックス エントリは、カタログ ビュー sys.indexes にインデックスの "type" 3 として表示されます。name 列には XML インデックスの名前が格納されています。

XML インデックスはカタログ ビュー sys.xml_indexes にも記録されます。このビューには、sys.indexes のすべての列および XML インデックスに有用な特定の列が含まれています。列 secondary_type に値 NULL がある場合はプライマリ XML インデックスであることを示します。セカンダリ XML インデックスの値 "P"、"R"、および "V" はそれぞれ PATH、PROPERTY、VALUE を示します。

XML インデックスによる領域の使用状況は、テーブル値関数 sys.dm_db_index_physical_stats でわかります。この関数は、すべてのインデックスの種類について、占有しているディスク ページの数、バイト単位の平均行サイズ、レコードの数などの情報を示します。また、この情報には XML インデックスも含まれています。この情報はデータベース パーティションごとに取得できます。どの XML インデックスにも同一の、ベース テーブルのパーティション構成とパーティション関数が使用されます。

XML スキーマ コレクションの取得

カタログ ビュー sys.xml_schema_collections には XML スキーマ コレクションが列挙されます。XML スキーマ コレクション "sys" がシステムにより定義されています。このコレクションには、すべてのユーザー定義 XML スキーマ コレクションで明示的に読み込むことなく使用できる定義済みの名前空間が含まれています。一覧には xml、xs、xsi、fn、および xdt 用の名前空間が含まれています。この他に、各 XML スキーマ コレクションのすべての名前空間を列挙する sys.xml_schema_namespaces、および各 XML スキーマのすべての XML スキーマ コンポーネントを列挙する sys.xml_components の 2 つのカタログ ビューがあります。

組み込み関数 XML_SCHEMA_NAMESPACE(schemaName, XmlSchemacollectionName, namespace-uri) により xml データ型のインスタンスが生成されます。このインスタンスには、XML スキーマ コレクションに含まれるスキーマ (定義済みの XML スキーマを除く) の XML スキーマ フラグメントが含まれます。

XML スキーマ コレクションのコンテンツは、次のようにして列挙できます。

  • XML スキーマ コレクションのカタログ ビューに対する Transact-SQL クエリを記述します。
  • 組み込み関数 XML_SCHEMA_NAMESPACE() を使用します。この関数の出力には xml データ型のメソッドを適用できます。ただし、基になる XML スキーマは変更できません。

このことを次の例で説明します。

例 : XML スキーマ コレクションでの XML 名前空間の列挙

XML スキーマ コレクション "myCollection" に次のクエリを実行します。

SELECT XSN.name
FROM    sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN
    ON (XSC.xml_collection_id = XSN.xml_collection_id)
WHERE    XSC.name = 'myCollection'   
例 : XML スキーマ コレクションのコンテンツの列挙

次のステートメントは、リレーショナル スキーマ dbo 内の XML スキーマ コレクション "myCollection" のコンテンツを列挙します。

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection')

XML_SCHEMA_NAMESPACE() の 3 番目の引数として対象になる名前空間を指定することで、コレクション内の個別の XML スキーマを xml データ型のインスタンスとして取得できます。次の例を参照してください。

例 : XML スキーマ コレクションからの指定したスキーマの出力

次のステートメントを実行すると、リレーショナル スキーマ dbo の XML スキーマ コレクション "myCollection" から、対象になる名前空間が "https://www.microsoft.com/books" である XML スキーマが出力されます。

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'https://www.microsoft.com/books')

XML スキーマへのクエリ

XML スキーマ コレクションに読み込んだ XML スキーマには、次のようにしてクエリを実行できます。

  • XML スキーマ名前空間のカタログ ビューに対する Transact-SQL クエリを記述します。
  • xml データ型の列を含むテーブルを作成し、その列に XML スキーマを保存して XML 型のシステムに読み込みます。その後、xml データ型のメソッドを使用して XML 列にクエリを実行できます。また、この列に XML インデックスを作成することもできます。ただしこの方法を使用する場合は、XML 列に保存されている XML スキーマと XML 型のシステムとの整合性をアプリケーションで保つ必要があります。たとえば、XML 型のシステムから XML スキーマ名前空間を削除する場合、整合性を保つためにテーブルからもその名前空間を削除する必要があります。

参照

関連項目

サーバー上の XML スキーマ コレクションの管理
XML データ型に対する XQuery 関数

概念

xml データ型

その他の技術情報

sys.dm_db_index_physical_stats
フルテキスト検索の概要

ヘルプおよび情報

SQL Server 2005 の参考資料の入手