FLWOR表達式是XQuery最重要的表達式之一。
FLWOR表達式是指由for、let、where、order by和return子句組成的表達式,可以把FLWOR念成flower。
在 SQL Server 2005 中,不支持 let。
FLWOR 語句和疊代 (XQuery)
XQuery 定義了 FLWOR 疊代語法。FLWOR 是 for、let、where、order by 和 return 的縮寫詞。在 SQL Server 2005 中,不支持 let。
FLWOR 語句由以下幾個部分組成:
輸入序列可以是其他 XQuery 表達式,例如 XPath 表達式。
可選的 where 子句。此子句將對疊代套用篩選謂詞。
可選的 order by 子句。
例如,以下查詢將在第一個生產位置對 <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="http://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
注意上述查詢的以下方面:
路徑表達式 //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 2005 中,不允許使用異類序列。尤其不允許使用由原子值和節點混合組成的序列。
在轉換 XML 格式時,疊代經常與 XML 構造語法一起使用,如下一個查詢中所示。
在 AdventureWorks 示例資料庫中,Production.ProductModel 表的 Instructions 列中存儲的生產說明的格式如下:
複製代碼
<Location LocationID="10" LaborHours="1.2"
SetupHours=".2" MachineHours=".1">
<step>describes 1st manu step</step>
<step>describes 2nd manu step</step>
...
</Location>
...
以下查詢將構造新的 XML,其中將 <Location> 元素與生產車間屬性一起作為子元素返回。
複製代碼
<Location>
<LocationID>10</LocationID>
<LaborHours>1.2</LaborHours>
<SetupHours>.2</SteupHours>
<MachineHours>.1</MachineHours>
</Location>
...
以下是查詢語句:
複製代碼
SELECT Instructions.query('
declare namespace AWMI="http://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>
...
使用 where 子句
可以使用 where 子句來篩選疊代結果。在下一個示例中使用 AdventureWorks 示例資料庫對此進行了說明。
在生產腳踏車時,生產過程經過了一系列生產車間。每個生產車間定義一個生產步驟序列。以下查詢僅檢索那些生產某個腳踏車型號並且生產步驟少於三步的生產車間。即,它們包含的 <step> 元素少於三個。
複製代碼
SELECT Instructions.query('
declare namespace AWMI="http://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 子句中的表達式的結果使用下列規則按指定的順序轉換為布爾值。這些規則與路徑表達式中的謂詞規則相同,只不過不允許使用整數:
如果 where 表達式返回一個空序列,則其有效的布爾值為 False。
如果 where 表達式返回一個簡單的布爾類型值,則該值為有效的布爾值。
如果 where 表達式返回至少包含一個節點的序列,則有效的布爾值為 True。
否則,它將出現靜態錯誤。
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)
')
注意上述查詢的以下方面:
/ManuInstructions/Location 和 $FirstStep in $Loc/Step[1] two表達式相互關聯,$FirstStep 的值取決於 $Loc 的值。
與 $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 namespace ="http://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 語句將構造帶有 <Step> 元素的 XML,該元素以生產步驟和 LocationID 作為其屬性。
在 XQuery prolog 中使用了“聲明默認元素命名空間”,以便在頂級元素顯示生成的 XML 中的所有名稱空間聲明。這使結果的可讀性更強。有關默認命名空間的詳細信息,請參閱處理 XQuery 中的命名空間。
下面是部分結果:
複製代碼
<Step xmlns=
"http://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=
"http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
LocationID="20">
Assemble all frame components following blueprint
<blueprint>1299</blueprint>.
</Step>
...
使用 order by 子句
通過使用 FLWOR 表達式中的 order by 子句可在 XQuery 中進行排序。傳遞到 order by 子句的排序表達式必須返回其類型對於 gt 運算符有效的值。每個排序表達式必須針對每一項生成一個單獨的序列。默認情況下,按升序進行排序。您也可以選擇為每個排序表達式指定升序或降序順序。
注意:
通過在 SQL Server 中實現 XQuery 進行的字元串值的排序比較始終是使用二進制 Unicode 碼位排序規則來執行的。
以下查詢將從 AdditionalContactInfo 列檢索有關特定客戶的所有電話號碼。結果按電話號碼進行排序。
複製代碼
SELECT AdditionalContactInfo.query('
declare namespace act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes";
declare namespace aci="http://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.Contact
WHERE ContactID=3
注意,原子化 (XQuery) 進程先檢索 <number> 元素的原子值,再將其傳遞到 order by。可以使用 data() 函式編寫表達式,但這並不是必需的。
複製代碼
order by data($a/act:number[1]) descending
結果如下:
複製代碼
<act:telephoneNumber xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
<act:number>333-333-3334</act:number>
</act:telephoneNumber>
<act:telephoneNumber xmlns:act="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes">
<act:number>333-333-3333</act:number>
</act:telephoneNumber>
可以使用 WITH XMLNAMESPACES 聲明命名空間,而不是在查詢 prolog 中聲明。
複製代碼
WITH XMLNAMESPACES (
'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes' AS act,
'http://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.Contact
WHERE ContactID=3
還可以按屬性值進行排序。例如,以下查詢將檢索新創建的 <Location> 元素,其中包含按 LaborHours 屬性以降序順序排序的 LocationID 和 LaborHours 屬性。結果,將首先返回工時最長的生產車間。
複製代碼
SELECT Instructions.query('
declare namespace AWMI="http://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="http://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" />
實現限制
限制如下:
不支持在 FLWOR 表達式中使用 let 。
排序表達式必須經過同類類型化。這是通過靜態檢查來確定的。
無法控制對空序列的排序。
不支持對 order by 使用 empty least、empty greatest 和 collation 關鍵字