XQuery 和靜態類型

適用於:SQL Server

SQL Server 中的 XQuery 是靜態類型語言。 也就是說,當運算式傳回具有特定函式或運算子不接受的類型或基數的值時,它會在查詢編譯期間引發類型錯誤。 此外,靜態類型檢查也可以偵測具型別 XML 檔上的路徑運算式是否設定錯誤。 XQuery 編譯器會先套用加入隱含作業的正規化階段,例如 Atomization,然後執行靜態類型推斷和靜態類型檢查。

靜態類型推斷

靜態類型推斷會決定運算式的傳回型別。 它會藉由取得輸入參數的靜態型別和作業的靜態語意,並推斷結果的靜態類型,來判斷這一點。 例如,運算式 1 + 2.3 的靜態類型是以下列方式決定:

  • 1 的靜態類型是 xs:integer ,而 2.3 的靜態類型是 xs:decimal 。 根據動態語意,作業的 + 靜態語意會將整數轉換成十進位,然後傳回十進位。 推斷的靜態類型接著會是 xs:decimal

對於不具類型的 XML 實例,有特殊類型表示資料未輸入。 這項資訊會在靜態類型檢查期間使用,並執行特定隱含轉換。

對於具類型的資料,輸入類型是從限制 XML 資料類型實例的 XML 架構集合推斷而來。 例如,如果架構只允許 xs:integer 類型的 元素,則使用該元素的路徑運算式結果會是 xs:integer 類型的 零個或多個專案。 這目前會使用運算式來表示,例如 element(age,xs:integer)* 星號 、 表示結果型別的基數。 在此範例中,運算式可能會導致名稱 「age」 和 type xs:integer 的零個或多個元素。 其他基數正好是一個,並且單獨使用類型名稱來表示,零或一個,並使用問號 ( ), 和 1 或更多,並使用加號表示 ( + )。

有時候,靜態類型推斷可以推斷運算式一律會傳回空序列。 例如,如果具型別 XML 資料類型上的路徑運算式在 customer 元素 (/customer > /name) 內 < 尋找 < name > 元素,但架構不允許 < 客戶 > 內 < 的名稱 > ,靜態類型推斷會推斷結果會是空的。 這會用來偵測不正確的查詢,而且會報告為靜態錯誤,除非運算式為 () 或 資料() 。

詳細的推斷規則是在 XQuery 規格的正式語意中提供。 Microsoft 只修改了這些屬性,以使用具類型的 XML 資料類型實例。 標準最重要的變更是隱含檔節點知道 XML 資料類型實例的類型。 因此,格式 /age 的路徑運算式會根據該資訊精確輸入。

藉由使用 SQL Server Profiler 範本和許可權 ,您可以看到在查詢編譯中傳回的靜態類型。 若要查看這些,您的追蹤必須包含 TSQL 事件類別目錄中的 XQuery 靜態類型事件。

靜態類型檢查

靜態類型檢查可確保執行時間執行只會接收作業適當類型的值。 由於型別不需要在執行時間檢查,因此可以在編譯初期偵測到潛在的錯誤。 這有助於改善效能。 不過,靜態類型需要查詢寫入器在制定查詢時更加小心。

以下是可以使用的適當類型:

  • 函式或作業明確允許的類型。

  • 明確允許類型的子類型。

子類型是根據 XML 架構的限制或延伸,使用衍生的子系結規則來定義。 例如,如果類型 S 的所有值也是類型 T 的實例,則類型 S 是類型 T 的子類型。

此外,所有整數值也是以 XML 架構類型階層為基礎的十進位值。 不過,並非所有的十進位值都是整數。 因此,整數是十進位的子類型,但反之亦然。 例如,此 + 作業只允許特定類型的值,例如數數值型別 xs:integer 、xs:decimal xs:float xs:double 。 如果傳遞其他型別的值,例如 xs:string ,則作業會引發類型錯誤。 這稱為強型別。 其他類型的值,例如用來表示不具類型 XML 的不可部分完成型別,可以隱含地轉換成作業接受的類型值。 這稱為弱式類型。

如果在隱含轉換之後需要它,靜態類型檢查可確保只有具有正確基數的允許型別值會傳遞至作業。 針對 「string」 + 1,它會辨識 「string」 的靜態類型為 xs:string 。 由於這不是作業的允許類型 + ,因此會引發類型錯誤。

將任意運算式 E1 的結果新增至任意運算式 E2(E1 + E2),靜態類型推斷會先判斷 E1 和 E2 的靜態類型,然後使用作業的允許類型檢查其靜態類型。 例如,如果 E1 的靜態類型可以是 xs:string xs:integer ,則靜態類型檢查會引發類型錯誤,即使執行時間的某些值可能是整數。 如果 E1 的靜態類型是 xs:integer* ,則情況也是如此。 +由於作業只接受一個整數值,而 E1 可以傳回零或大於 1,所以靜態類型檢查會產生錯誤。

如先前所述,類型推斷經常推斷比使用者所知道的要傳遞之資料類型更廣的類型。 在這些情況下,使用者必須重寫查詢。 一些典型案例包括下列專案:

  • 類型會推斷更一般的類型,例如超型別或類型的聯集。 如果類型是不可部分完成的類型,您應該使用轉換運算式或建構函式來指出實際的靜態類型。 例如,如果運算式 E1 的推斷類型是 xs:string 或 xs:integer 之間的 選擇,而且加法需要 xs:integer ,則您應該寫入 xs:integer(E1) + E2 而不是 E1+E2 如果遇到無法轉換成 xs:integer 的字串值,則此運算式可能會在執行時間失敗。 不過,運算式現在會傳遞靜態類型檢查。 此運算式會對應至空序列。

  • 型別會推斷出比實際包含的資料更高的基數。 這種情況經常發生,因為 xml 資料類型可以包含一個以上的最上層專案,而且 XML 架構集合無法限制這個。 為了減少靜態類型,並保證至少傳遞一個值,您應該使用位置述 [1] 詞 。 例如,若要將 1 新增至專案最上層下元素的 屬性值 c ,您必須 write (/a/b/@c)[1]+1b 此外,DOCUMENT 關鍵字可以與 XML 架構集合搭配使用。

  • 某些作業會在推斷期間遺失類型資訊。 例如,如果無法判斷節點的類型,它就會 變成 anyType 。 這不會隱含轉換成任何其他類型。 這些轉換最常在流覽期間使用父軸進行。 如果運算式將建立靜態類型錯誤,您應該避免使用這類作業並重寫查詢。

聯集類型的類型檢查

聯集類型需要謹慎處理,因為類型檢查。 下列範例說明其中兩個問題。

範例:函式 over Union Type

請考慮等位型別的專案定義 <r> :

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

在 XQuery 內容中,「average」 函 fn:avg (//r) 式會傳回靜態錯誤,因為 XQuery 編譯器無法為 <>r fn:avg() 自 變數中的元素新增不同類型的值( xs:int xs:float xs:double)。 若要解決此問題,請將函式調用重寫為 fn:avg(for $r in //r return $r cast as xs:double ?)

範例:運算子 over Union Type

加法運算 ('+') 需要精確的運算元類型。 因此,運算式 (//r)[1] + 1 會傳回靜態錯誤,該錯誤具有先前所描述的專案 <r> 型別定義。 其中一個解決方案是將它重寫為 (//r)[1] cast as xs:int? +1 ,其中 「?」 表示出現 0 或 1 次。 SQL Server 需要 「cast as」 與 「?」,因為任何轉換都可能導致執行階段錯誤而造成空序列。

另請參閱

Xquery 語言參考 (SQL Server)