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]+1
。b
此外,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」 與 「?」,因為任何轉換都可能導致執行階段錯誤而造成空序列。