分享方式:


FLWOR 語句和反復專案 (XQuery)

適用於:SQL Server

XQuery 會定義 FLWOR 反復專案語法。 FLWOR 是 、 letwhereorder byreturnfor 縮寫。

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

  • 將一或多個反覆運算器變數系結至輸入序列的一或多個 FOR 子句。

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

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

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

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

  • 選擇性的 order by 子句。

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

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

declare @x 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 會產生輸入序列。 此序列是第一個 Location>< 元素節點之元素節點子系的順序。 <step>

  • 不使用選擇性述詞子句 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 xml  
set @x=''  
SELECT @x.query('  
for $a in (1, 2, 3)  
  return $a')  
-- result = 1 2 3   
  
declare @x 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 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 建構 語法搭配使用,如下一個查詢所示。

在 AdventureWorks 範例資料庫中,儲存在 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 變數的運算式會在每次查詢中參考變數時插入查詢中。 這表示語句的執行次數會和參考運算式一樣多。

AdventureWorks2022 資料庫中,製造指示包含所需工具的相關資訊,以及使用工具的位置。 下列查詢會 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 運算式會傳回每個 <Location> 元素中的第一個 <Step> 元素子系。

declare @x 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 變數。

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

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

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

以下是結果:

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

下列查詢類似,不同之處在于它會針對 ProductModel 資料表的 Instructions 資料行、具類型的 xml 資料行指定。 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 句會定義兩個變數和 $S$WC 。 與 $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;  

請注意, Atomization (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>Almuminum Alloy</Material>  
<ProductLine>Mountain bike</ProductLine>  
<RiderExperience>Advanced to Professional riders</RiderExperience>  
<Style>Unisex</Style>    

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

declare @x 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 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" />  

實作限制

以下是限制:

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

  • 無法控制排序空序列。

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

另請參閱

XQuery 運算式