XQuery と静的な型指定
SQL Server の XQuery は静的型指定の言語です。 つまり、特定の関数または演算子が許容できない型または基数を持つ値が式から返される場合、クエリのコンパイル時に型エラーを生成します。 また静的な型チェックでは、型指定された XML ドキュメントのパス式に型の誤りがあるとそれを検出します。 XQuery コンパイラは、アトミック化などの暗黙的な操作を加える正規化を適用してから、静的な型の推定および確認を行います。
静的な型の推定
静的な型の推定では、式の戻り型が判定されます。 この判定は、入力パラメーターの静的な型および演算の静的なセマンティクスを取得し、結果の静的な型を推定することで行われます。 たとえば、式 1 + 2.3 の静的な型は次のようにして判定されます。
- 1 の静的な型は xs:integer、2.3 の静的な型は xs:decimal です。 動的なセマンティクスに基づくと、+ 演算の静的なセマンティクスは integer 型の値を decimal 型の値に変換し、decimal 型の値を返すことになります。 したがって、静的な型として xs:decimal が推定されます。
型指定されていない XML インスタンスのために、データが型指定されていないことを示す特別な型があります。 その情報は、静的な型チェックのときに暗黙的に型をキャストするために使用されます。
型指定されたデータの場合、XML データ型インスタンスを制約する XML スキーマ コレクションから入力の型が推定されます。 たとえば、スキーマで xs:integer 型の要素のみを許可している場合、その要素を使用するパス式の結果には xs:integer 型の要素が 0 個以上含まれることになります。 現時点ではこれを element(age,xs:integer)* のような式で表現します。アスタリスク (*) は結果の型の基数を示します。 この例では、式の結果が 0 個以上の "age" という名前の xs:integer 型の要素になります。 基数が 1 の場合は型名のみ、0 または 1 の場合は疑問符 (?)、1 以上の場合はプラス記号 (+) で表現します。
静的な型の推定により、式が常に空のシーケンスを返すことを推定できる場合があります。 たとえば、型指定された XML データ型のパス式で <customer> 要素内の <name> 要素 (/customer/name) を検索する場合、スキーマで <customer> 内の <name> を許可していなければ、結果が空になると推定されます。 この情報を使用して不適切なクエリを検出され、式が () または data( () ) でない限り、静的なエラーとして報告されます。
推定規則の詳細は、XQuery 仕様の正式なセマンティクスで定義されています。 マイクロソフトでは、型指定された XML データ型のインスタンスに対応するためにこの規則を少しだけ変更しました。 標準からの最も重要な変更点は、暗黙的なドキュメント ノードで XML データ型のインスタンスの型を認識することです。 その結果、認識された情報を基に /age のような形式のパス式も正確に型指定されます。
SQL Server プロファイラーのテンプレートと権限 を使用すると、クエリのコンパイル時に返される静的な型を確認できます。 これらを確認するには、トレースに TSQL イベント カテゴリの XQuery Static Type イベントが含まれている必要があります。
静的な型チェック
静的な型チェックにより、ランタイムの実行時に、演算に適合する型の値のみを受け取れるようになります。 実行時には必ずしも型がチェックされるとは限りませんが、コンパイルの初期段階で潜在的なエラーを検出できます。 その結果パフォーマンスを向上できます。 ただし、静的な型指定を使用する場合、クエリの記述には通常以上の配慮が必要になります。
次に、使用できる適切な型を示します。
関数または演算により明示的に許可されている型。
明示的に許可されている型のサブタイプ。
サブタイプは、XML スキーマの制約または拡張による派生を使用するためのサブタイプ指定規則に基づいて定義されます。 たとえば、型が S であるすべての値が型 T のインスタンスでもある場合、型 S は型 T のサブタイプです。
また、XML スキーマの型階層に基づき、integer 型のすべての値は decimal 型の値でもあります。 一方、decimal 型のすべての値が integer 型の値であるとは限りません。 したがって、integer 型は decimal 型のサブタイプですが、逆は違います。 たとえば + 演算では、数値型の xs:integer、xs:decimal、xs:float、xs:double など、特定の型の値のみが許可されます。 xs:string など、それ以外の型の値が渡された場合は型エラーが発生します。 これを厳密な型指定と呼びます。 また、型指定されていない XML を示すアトミック型など、他の型の値から演算で受け入れることのできる型の値に暗黙的に変換することができます。 これを弱い型指定と呼びます。
暗黙的な変換の後での静的な型チェックを必須にした場合、基数が正しく、許可された型の値のみが演算に渡されます。 "文字列" + 1 の場合、"文字列" の静的な型は xs:string と認識されます。 この型は + 演算では許可されていないので、型エラーが発生します。
任意の式 E1 の結果に任意の式 E2 を加算する (E1 + E2) 場合、静的な型の推定によって E1 および E2 の静的な型が判定されたうえで、それらの型が演算に許可されている型であるかどうかのチェックが行われます。 たとえば、E1 の静的な型が xs:string または xs:integer になる可能性がある場合、実行時の値は整数になるかもしれませんが、静的な型チェックによって型エラーが発生します。 E1 の静的な型が xs:integer* の場合も同様の結果になります。 + 演算で受け取れるのは 1 つの整数値のみですが、E1 は 0 または 1 以上の値を返す可能性があるので、静的な型チェックによりエラーが発生します。
既に説明したように、型の推定の結果、渡されるデータの型についてユーザーが認識しているよりも範囲の広い型が導かれることがよくあります。 そのような場合は、ユーザーがクエリを書き換える必要があります。 よく見られるケースを次に示します。
スーパータイプまたは型の共用体など、汎化された型が推定される。 アトミック型の場合は、キャスト式またはコンストラクター関数を使用して実際の静的な型を指定してください。 たとえば、推定された式 E1 の型が xs:string または xs:integer のいずれになり得る場合、加算に xs:integer が必要であれば、「E1+E2」ではなく「xs:integer(E1) + E2」と記述します。 この式は、xs:integer にキャストできない文字列値が検出されると、実行時に失敗する可能性があります。 ただし、この式は静的な型チェックには合格します。 SQL Server 2005 以降では、この式が空のシーケンスにマップされます。
データの実際の内容を上回る基数が推定される。 xml データ型には 1 つ以上の最上位要素を含めることはできますが、それを XML スキーマ コレクションで制約することはできないので、このケースは頻繁に発生します。 静的な型を限定し、渡される値の数を 1 以下にするためには、位置関係の述語 [1] を使用してください。 たとえば、最上位要素 a の下にある要素 b の属性 c の値に 1 を加算するには、「(/a/b/@c)[1]+1」と記述する必要があります。 また、XML スキーマ コレクションと共に DOCUMENT キーワードを使用できます。
一部の演算が推定時に型の情報を失う。 たとえば、ノードの型を判別できない場合、anyType 型になります。 この型は他の型に暗黙的にキャストされることはありません。 これらの変換は、parent 軸を使用したナビゲーションで特に発生します。 式で静的な型のエラーが発生する場合、そのような演算の使用は避け、クエリを書き換えてください。
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) が fn:avg() の引数として受け取る <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 以降の SQL Server では、すべてのキャストは実行時エラーの結果として空のシーケンスになる可能性があるので、"cast as" に "?" を付ける必要があります。