共用方式為


FLWOR 陳述式及疊代 (XQuery)

適用於:SQL Server

XQuery 會定義 FLWOR 反覆項目語法。 FLWOR 是 、forletwhereorder byreturn縮寫。

FLWOR 語句是由下列部分所組成:

  • 將一或多個迭代器變數繫結至輸入序列的一或多個 FOR 子句。

    輸入序列可以是其他 XQuery 運算式,例如 XPath 表達式。 它們要麼是節點序列,要麼是原子值序列。 不可部分完成的值序列可以使用常值或建構函式來建構。 建構的 XML 節點不允許作為 SQL Server 中的輸入序列。

  • 選擇性的 let 子句。 這個子句會將值指派給特定反覆專案的指定變數。 指派的表達式可以是 XQuery 運算式,例如 XPath 運算式,而且可以傳回節點序列或不可部分完成值序列。 不可部分完成的值序列可以使用常值或建構函式來建構。 建構的 XML 節點不允許作為 SQL Server 中的輸入序列。

  • 反覆運算器變數。 此變數可以使用 關鍵詞來擁有選擇性的類型判斷提示 as

  • 選擇性的 where 子句。 這個子句會在反覆專案上套用篩選述詞。

  • 選擇性的 order by 子句。

  • return 運算式。 子句中的 return 表達式會建構 FLWOR 語句的結果。

例如,下列查詢會逐一查看第一個製造位置的專案 <Step> ,並傳回節點的 <Step> 字串值:

DECLARE @x AS XML;

SET @x = '<ManuInstructions ProductModelID="1" ProductModelName="SomeBike" >
<Location LocationID="L1" >
  <Step>Manu step 1 at Loc 1</Step>
  <Step>Manu step 2 at Loc 1</Step>
  <Step>Manu step 3 at Loc 1</Step>
</Location>
<Location LocationID="L2" >
  <Step>Manu step 1 at Loc 2</Step>
  <Step>Manu step 2 at Loc 2</Step>
  <Step>Manu step 3 at Loc 2</Step>
</Location>
</ManuInstructions>';

SELECT @x.query('
   for $step in /ManuInstructions/Location[1]/Step
   return string($step)
');

結果如下︰

Manu step 1 at Loc 1 Manu step 2 at Loc 1 Manu step 3 at Loc 1

下列查詢與前一個查詢類似,不同之處在於它是針對 ProductModel 資料表的 Instructions 資料行 (類型化 xml 資料行) 指定的。 查詢會逐一查看特定產品第一個工作中心位置的所有製造步驟、 <step> 元素。

SELECT Instructions.query('
   declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
for $Step in //AWMI:root/AWMI:Location[1]/AWMI:step
      return
           string($Step)
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

請注意下列項目是從上一個查詢而來:

  • $Step是反覆運算器變數。

  • 路徑表達式//AWMI:root/AWMI:Location[1]/AWMI:step 會產生輸入序列。 此序列是第一個<step>元素節點之元素節點子系的順序。<Location>

  • 不會使用選擇性述詞子句。 where

  • 表達式會 return 從 <step> 專案傳回字串值。

字串函式 (XQuery) 用來擷取節點的<step>字串值。

這是部分結果:

Insert aluminum sheet MS-2341 into the T-85A framing tool.
Attach Trim Jig TJ-26 to the upper and lower right corners of
the aluminum sheet. ....

以下是允許的其他輸入序列範例:

DECLARE @x AS XML;

SET @x = '';

SELECT @x.query('
for $a in (1, 2, 3)
  return $a');
-- result = 1 2 3

DECLARE @x AS XML;

SET @x = '';

SELECT @x.query('
for $a in
   for $b in (1, 2, 3)
      return $b
return $a');
-- result = 1 2 3

DECLARE @x AS XML;

SET @x = '<ROOT><a>111</a></ROOT>';

SELECT @x.query('
  for $a in (xs:string( "test"), xs:double( "12" ), data(/ROOT/a ))
  return $a');
-- result test 12 111

在 SQL Server 中,不允許異質序列。 具體而言,不允許包含原子值和節點混合的序列。

在轉換 XML 格式時,反覆專案經常與 XML 建構 (XQuery) 語法一起使用,如下一個查詢所示。

在 AdventureWorks 範例資料庫中,儲存在資料表資料Instructions行中的Production.ProductModel製造指示具有下列形式:

<Location LocationID="10" LaborHours="1.2"
            SetupHours=".2" MachineHours=".1">
  <step>describes 1st manu step</step>
   <step>describes 2nd manu step</step>
   ...
</Location>
...

下列查詢會建構新的 XML,此 XML 具有 <Location> 以子元素傳回的工作中心位置屬性:

<Location>
   <LocationID>10</LocationID>
   <LaborHours>1.2</LaborHours>
   <SetupHours>.2</SetupHours>
   <MachineHours>.1</MachineHours>
</Location>
...

這是查詢:

SELECT Instructions.query('
     declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
        for $WC in /AWMI:root/AWMI:Location
        return
          <Location>
            <LocationID> { data($WC/@LocationID) } </LocationID>
            <LaborHours>   { data($WC/@LaborHours) }   </LaborHours>
            <SetupHours>   { data($WC/@SetupHours) }   </SetupHours>
            <MachineHours> { data($WC/@MachineHours) } </MachineHours>
          </Location>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

請注意上一個查詢中的下列考量:

  • FLWOR 語句會擷取特定產品的元素序列 <Location> 。

  • 資料函數 (XQuery) 可用來擷取每個屬性的值,因此會將它們作為文字節點 (而不是屬性) 新增至產生的 XML。

  • 子句中的 RETURN 運算式會建構您想要的 XML。

這是部分結果:

<Location>
  <LocationID>10</LocationID>
  <LaborHours>2.5</LaborHours>
  <SetupHours>0.5</SetupHours>
  <MachineHours>3</MachineHours>
</Location>
<Location>
   ...
<Location>
...

使用子 let

您可以使用 let 子句來命名重複表達式,您可以藉由參考 變數來命名。 指派給 let 變數的表示式會在每次查詢中參考變數時插入查詢中。 這表示語句的執行次數會和參考表達式一樣多。

AdventureWorks2025 資料庫中,製造指示包含所需工具的相關信息,以及使用工具的位置。 下列查詢會使用子 let 句來列出建置生產模型所需的工具,以及需要每個工具的位置。

SELECT Instructions.query('
     declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
        for $T in //AWMI:tool
            let $L := //AWMI:Location[.//AWMI:tool[.=data($T)]]
        return
          <tool desc="{data($T)}" Locations="{data($L/@LocationID)}"/>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

使用子 where

您可以使用 where 子句來篩選反覆項目的結果。 下一個範例會說明這一點。

在自行車的製造中,製造過程經過一系列工作中心位置。 每個工作中心位置都會定義一連串的製造步驟。 下列查詢只會擷取製造自行車型號且少於三個製造步驟的工作中心位置。 也就是說,它們少於三 <step> 個元素。

SELECT Instructions.query('
     declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
for $WC in /AWMI:root/AWMI:Location
      where count($WC/AWMI:step) < 3
      return
          <Location >
           { $WC/@LocationID }
          </Location>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

下列為上一個查詢的注意事項:

  • 關鍵字where會使用函數count()來計算每個工作中心位置中的子元素數目<step>。

  • 表達式 return 會從反覆項目的結果建構您想要的 XML。

結果如下︰

<Location LocationID="30"/>

子句中的 where 表達式結果會依照指定的順序,使用下列規則轉換成布爾值。 這些與路徑運算式中述詞的規則相同,只是不允許整數:

  1. where如果表達式傳回空序列,則其有效布爾值為 False。

  2. 如果表達式傳 where 回一個簡單的布爾型別值,該值就是有效的布爾值。

  3. where如果表達式傳回至少包含一個節點的序列,有效的布爾值為 True。

  4. 否則,它會引發靜態錯誤。

FLWOR 中的多變量綁定

您可以有將多個變數系結至輸入序列的單一 FLWOR 運算式。 在下列範例中,會針對不具類型的 xml 變數指定查詢。 FLOWR 運算式會傳回每個<Step>元素中的第一個<Location>元素子系。

DECLARE @x AS XML;

SET @x = '<ManuInstructions ProductModelID="1" ProductModelName="SomeBike" >
<Location LocationID="L1" >
  <Step>Manu step 1 at Loc 1</Step>
  <Step>Manu step 2 at Loc 1</Step>
  <Step>Manu step 3 at Loc 1</Step>
</Location>
<Location LocationID="L2" >
  <Step>Manu step 1 at Loc 2</Step>
  <Step>Manu step 2 at Loc 2</Step>
  <Step>Manu step 3 at Loc 2</Step>
</Location>
</ManuInstructions>';

SELECT @x.query('
   for $Loc in /ManuInstructions/Location,
       $FirstStep in $Loc/Step[1]
   return
       string($FirstStep)
');

請注意下列項目是從上一個查詢而來:

  • 表達式 for$Loc 定義 和 $FirstStep 變數。

  • 和的運算式相互關聯,其值two相依於 的值/ManuInstructions/Location$FirstStep in $Loc/Step[1]$FirstStep$Loc

  • 與關聯的$Loc表達式會產生一連串的專案<Location>。 針對每個 <Location> 元素, $FirstStep 產生一 <Step> 個元素的序列,一個單一元素。

  • $Loc 是在與 $FirstStep 變數相關聯的表達式中指定。

結果如下︰

Manu step 1 at Loc 1
Manu step 1 at Loc 2

下列查詢類似,不同之處在於它是針對資料表的 Instructions 資料行、類型化 ProductModel 資料行指定的。 XML 建構 (XQuery) 可用來產生您想要的 XML。

SELECT Instructions.query('
     declare default element namespace "https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
for $WC in /root/Location,
            $S  in $WC/step
      return
          <Step LocationID= "{$WC/@LocationID }" >
            { $S/node() }
          </Step>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

下列為上一個查詢的注意事項:

  • for句會定義兩個變數和 $WC$S 。 與 $WC 相關聯的表達式會在自行車產品模型的製造中產生一連串的工作中心位置。 指派給 $S 變數的路徑表達式會針對 中的 $WC每個工作中心位置順序產生一連串的步驟。

  • return 陳述式建構 XML,其元素包含 <Step> 製造步驟,且 作為 LocationID 其屬性。

  • 宣告預設元素命名空間用於 XQuery 初構中,讓產生的 XML 中的所有命名空間宣告都出現在最上層元素。 這可讓結果更容易閱讀。 如需預設命名空間的詳細資訊,請參閱 在 XQuery 中處理命名空間。

這是部分結果:

<Step xmlns=
    "https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
  LocationID="10">
     Insert <material>aluminum sheet MS-2341</material> into the <tool>T-
     85A framing tool</tool>.
</Step>
...
<Step xmlns=
      "https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
    LocationID="20">
        Assemble all frame components following blueprint
        <blueprint>1299</blueprint>.
</Step>
...

使用子 order by

XQuery 中的排序是使用 order by FLWOR 運算式中的 子句來執行。 傳遞給子句的 order by 排序運算式必須傳回類型對運算子有效的 gt 值。 每個排序表達式都必須產生一個具有一個專案的序列。 根據預設,會以遞增順序執行排序。 您可以選擇性地為每個排序表示式指定遞增或遞減順序。

注意

對 SQL Server 中 XQuery 實作所執行的字串值進行排序比較,一律使用二進位 Unicode 字碼點定序來執行。

下列查詢會從 AdditionalContactInfo 數據行擷取特定客戶的所有電話號碼。 結果會依電話號碼排序。

USE AdventureWorks2022;
GO

SELECT AdditionalContactInfo.query('
   declare namespace act="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes";
   declare namespace aci="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo";
   for $a in /aci:AdditionalContactInfo//act:telephoneNumber
   order by $a/act:number[1] descending
   return $a
') AS Result
FROM Person.Person
WHERE BusinessEntityID = 291;

原子化 (XQuery) 程序會擷取元素的<number>原子值,然後再將其傳遞給 。order by 您可以使用函數 data() 來撰寫運算式,但這不是必要的。

order by data($a/act:number[1]) descending

結果如下︰

<act:telephoneNumber xmlns:act="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
  <act:number>333-333-3334</act:number>
</act:telephoneNumber>
<act:telephoneNumber xmlns:act="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
  <act:number>333-333-3333</act:number>
</act:telephoneNumber>

您可以使用 來宣告命名 WITH XMLNAMESPACES空間,而不是在查詢序言中宣告命名空間。

WITH XMLNAMESPACES ('https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes' AS act, 'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo' AS aci)
SELECT AdditionalContactInfo.query('
   for $a in /aci:AdditionalContactInfo//act:telephoneNumber
   order by $a/act:number[1] descending
   return $a
') AS Result
FROM Person.Person
WHERE BusinessEntityID = 291;

您也可以依屬性值排序。 例如,下列查詢會擷取新建立 <Location> 的元素,這些元素的LocationID和LaborHours屬性會以遞減順序依 LaborHours 屬性排序。 因此,會先傳回具有最大工時的工作中心位置。

SELECT Instructions.query('
     declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
for $WC in /AWMI:root/AWMI:Location
order by $WC/@LaborHours descending
        return
          <Location>
             { $WC/@LocationID }
             { $WC/@LaborHours }
          </Location>
') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 7;

結果如下︰

<Location LocationID="60" LaborHours="4"/>
<Location LocationID="50" LaborHours="3"/>
<Location LocationID="10" LaborHours="2.5"/>
<Location LocationID="20" LaborHours="1.75"/>
<Location LocationID="30" LaborHours="1"/>
<Location LocationID="45" LaborHours=".5"/>

在下列查詢中,結果會依專案名稱排序。 查詢會從產品目錄擷取特定產品的規格。 規格是 專案的子 <Specifications> 系。

SELECT CatalogDescription.query('
     declare namespace
 pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
      for $a in /pd:ProductDescription/pd:Specifications/*
     order by local-name($a)
      return $a
    ') AS Result
FROM Production.ProductModel
WHERE ProductModelID = 19;

請注意下列項目是從上一個查詢而來:

  • 表達式會 /p1:ProductDescription/p1:Specifications/* 傳回 的項目 <Specifications>子系。

  • 表達式會 order by (local-name($a)) 依項目名稱的局部部分來排序序列。

結果如下︰

<Color>Available in most colors</Color>
<Material>Aluminum Alloy</Material>
<ProductLine>Mountain bike</ProductLine>
<RiderExperience>Advanced to Professional riders</RiderExperience>
<Style>Unisex</Style>

排序表達式傳回空白的節點會排序為序列的開頭,如下列範例所示:

DECLARE @x AS XML;

SET @x = '<root>
  <Person Name="A" />
  <Person />
  <Person Name="B" />
</root>
';

SELECT @x.query('
  for $person in //Person
  order by $person/@Name
  return   $person
');

結果如下︰

<Person />
<Person Name="A" />
<Person Name="B" />

您可以指定多個排序準則,如下列範例所示。 此範例中的查詢會先依 Title 排序元素,再依 Administrator 屬性值排序 <Employee> 。

DECLARE @x AS XML;

SET @x = '<root>
  <Employee ID="10" Title="Teacher"        Gender="M" />
  <Employee ID="15" Title="Teacher"  Gender="F" />
  <Employee ID="5" Title="Teacher"         Gender="M" />
  <Employee ID="11" Title="Teacher"        Gender="F" />
  <Employee ID="8" Title="Administrator"   Gender="M" />
  <Employee ID="4" Title="Administrator"   Gender="F" />
  <Employee ID="3" Title="Teacher"         Gender="F" />
  <Employee ID="125" Title="Administrator" Gender="F" /></root>';

SELECT @x.query('for $e in /root/Employee
order by $e/@Title ascending, $e/@Gender descending

  return
     $e
');

結果如下︰

<Employee ID="8" Title="Administrator" Gender="M" />
<Employee ID="4" Title="Administrator" Gender="F" />
<Employee ID="125" Title="Administrator" Gender="F" />
<Employee ID="10" Title="Teacher" Gender="M" />
<Employee ID="5" Title="Teacher" Gender="M" />
<Employee ID="11" Title="Teacher" Gender="F" />
<Employee ID="15" Title="Teacher" Gender="F" />
<Employee ID="3" Title="Teacher" Gender="F" />

局限性

以下是限制:

  • 排序表達式必須是同質型別。 這是靜態檢查。

  • 無法控制對空序列進行排序。

  • 不支援空白的最小值、空白的最大值和定序關鍵字 on order by