Příkaz FLWOR a iterace (XQuery)
platí pro:SQL Server
XQuery definuje syntaxi iterace FLWOR. FLWOR je zkratka pro for
, let
, where
, order by
a return
.
Prohlášení FLWOR se skládá z následujících částí:
Jedna nebo více klauzulí FOR, které sváže jednu nebo více proměnných iterátoru se vstupními sekvencemi.
Vstupní sekvence můžou být jiné výrazy XQuery, jako jsou výrazy XPath. Jedná se o sekvence uzlů nebo posloupnosti atomických hodnot. Sekvence atomických hodnot lze vytvořit pomocí literálů nebo funkcí konstruktoru. Vytvořené uzly XML nejsou povoleny jako vstupní sekvence na SQL Serveru.
Volitelná klauzule
let
. Tato klauzule přiřadí danou proměnnou hodnotu pro konkrétní iteraci. Přiřazený výraz může být výraz XQuery, například výraz XPath, a může vracet posloupnost uzlů nebo posloupnost atomických hodnot. Sekvence atomických hodnot lze vytvořit pomocí literálů nebo funkcí konstruktoru. Vytvořené uzly XML nejsou povoleny jako vstupní sekvence na SQL Serveru.Proměnná iterátoru. Tato proměnná může mít volitelný kontrolní výraz typu pomocí klíčového slova
as
.Volitelná klauzule
where
. Tato klauzule aplikuje predikát filtru na iteraci.Volitelná klauzule
order by
.Výraz
return
. Výraz v klauzulireturn
vytvoří výsledek příkazu FLWOR.
Například následující dotaz iteruje prvky <Step
> v prvním výrobním umístění a vrátí řetězcovou hodnotu <Step
> uzlů:
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)
')
Toto je výsledek:
Manu step 1 at Loc 1 Manu step 2 at Loc 1 Manu step 3 at Loc 1
Následující dotaz se podobá předchozímu dotazu, s tím rozdílem, že je zadán ve sloupci Pokyny, zadaný sloupec XML, v tabulce ProductModel. Dotaz iteruje všechny výrobní kroky, <step
> prvky, v prvním umístění pracovního centra pro konkrétní produkt.
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
Všimněte si následujících věcí z předchozího dotazu:
$Step
je proměnná iterátoru.Výraz cesty ,
//AWMI:root/AWMI:Location[1]/AWMI:step
, vygeneruje vstupní sekvenci. Tato sekvence je posloupnost podřízeného uzlu elementu <step
> prvního uzlu elementu <Location
>.Nepovinná predikátová klauzule,
where
, se nepoužívá.Výraz
return
vrátí řetězcovou hodnotu z elementu <step
>.
řetězcové funkce (XQuery) se používá k načtení řetězcové hodnoty uzlu <step
>.
Toto je částečný výsledek:
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. ....
Tady jsou příklady dalších vstupních sekvencí, které jsou povolené:
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
V SQL Serveru nejsou heterogenní sekvence povoleny. Konkrétně nejsou povoleny sekvence, které obsahují kombinaci atomických hodnot a uzlů.
Iterace se často používá společně se syntaxí XML Construction při transformaci formátů XML, jak je znázorněno v dalším dotazu.
V ukázkové databázi AdventureWorks mají výrobní pokyny uložené ve sloupci Pokyny tabulky Production.ProductModel následující formulář:
<Location LocationID="10" LaborHours="1.2"
SetupHours=".2" MachineHours=".1">
<step>describes 1st manu step</step>
<step>describes 2nd manu step</step>
...
</Location>
...
Následující dotaz vytvoří nový xml, který má <Location
> elementy s atributy umístění pracovního centra vrácenými jako podřízené prvky:
<Location>
<LocationID>10</LocationID>
<LaborHours>1.2</LaborHours>
<SetupHours>.2</SetupHours>
<MachineHours>.1</MachineHours>
</Location>
...
Toto je dotaz:
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
Všimněte si následujících věcí z předchozího dotazu:
Příkaz FLWOR načte posloupnost <
Location
> prvků pro konkrétní produkt.datová funkce (XQuery) slouží k extrahování hodnoty každého atributu, aby se přidaly do výsledného XML jako textové uzly místo atributů.
Výraz v klauzuli RETURN vytvoří požadovaný kód XML.
Jedná se o částečný výsledek:
<Location>
<LocationID>10</LocationID>
<LaborHours>2.5</LaborHours>
<SetupHours>0.5</SetupHours>
<MachineHours>3</MachineHours>
</Location>
<Location>
...
<Location>
...
Použití klauzule let
Klauzuli let
můžete použít k pojmenování opakujících se výrazů, na které můžete odkazovat odkazováním na proměnnou. Výraz přiřazený proměnné let
se vloží do dotazu při každém odkazování na proměnnou v dotazu. To znamená, že příkaz se spustí tolikrát, kolikrát se na výraz odkazuje.
V AdventureWorks2022
databázi obsahují pokyny pro výrobu informace o požadovaných nástrojích a umístění, kde se nástroje používají. Následující dotaz používá klauzuli let
k výpisu nástrojů potřebných k sestavení produkčního modelu a také umístění, kde je každý nástroj potřebný.
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
Použití klauzule where
Pomocí klauzule where
můžete filtrovat výsledky iterace. To je znázorněno v tomto dalším příkladu.
Při výrobě jízdních kol prochází výrobní proces řadou pracovních center. Každé umístění pracovního centra definuje posloupnost výrobních kroků. Následující dotaz načte pouze umístění pracovních center, která vyrábí model jízdních kol a mají méně než tři výrobní kroky. To znamená, že mají méně než tři prvky <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
Všimněte si následující položky v předchozím dotazu:
Klíčové slovo
where
používá funkci count() k počítání počtu podřízených prvků <step
> v každém umístění pracovního centra.Výraz
return
vytvoří požadovaný kód XML z výsledků iterace.
Toto je výsledek:
<Location LocationID="30"/>
Výsledek výrazu v klauzuli where
se převede na logickou hodnotu pomocí následujících pravidel v uvedeném pořadí. Jsou stejné jako pravidla pro predikáty ve výrazech cesty s tím rozdílem, že celá čísla nejsou povolená:
Pokud výraz
where
vrátí prázdnou sekvenci, jeho efektivní logická hodnota je False.Pokud výraz
where
vrátí jednu jednoduchou logickou hodnotu typu, jedná se o efektivní logickou hodnotu.Pokud výraz
where
vrátí sekvenci, která obsahuje alespoň jeden uzel, efektivní logická hodnota je True.V opačném případě vyvolá statickou chybu.
Více proměnných vazeb v FLWOR
Můžete mít jeden výraz FLWOR, který vytvoří vazbu více proměnných na vstupní sekvence. V následujícím příkladu je dotaz určen pro netypovou proměnnou XML. Výraz FLOWR vrátí první podřízený prvek <Step
> v každém prvku <Location
>.
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)
')
Všimněte si následujících věcí z předchozího dotazu:
Výraz
for
definuje proměnné$Loc
a $FirstStep
.Výrazy
two
,/ManuInstructions/Location
a$FirstStep in $Loc/Step[1]
korelují v tom, že hodnoty$FirstStep
závisejí na hodnotách$Loc
.Výraz přidružený k
$Loc
vygeneruje sekvenci prvků <Location
>. Pro každý prvek <Location
>$FirstStep
vygeneruje posloupnost jednoho <Step
> elementu singleton.$Loc
je zadán ve výrazu přidruženém k proměnné$FirstStep
.
Toto je výsledek:
Manu step 1 at Loc 1
Manu step 1 at Loc 2
Následující dotaz je podobný, s tím rozdílem, že je určen pro sloupec Pokyny, zadejte xml sloupec ProductModel tabulka. XML Construction (XQuery) slouží k vygenerování požadovaného 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
Všimněte si následující položky v předchozím dotazu:
Klauzule
for
definuje dvě proměnné,$WC
a$S
. Výraz přidružený k$WC
vygeneruje sekvenci umístění pracovních center při výrobě modelu výrobku jízdních kol. Výraz cesty přiřazený proměnné$S
generuje posloupnost kroků pro každou sekvenci umístění pracovního centra v$WC
.Příkaz return vytvoří XML, který má <
Step
> element, který obsahuje výrobní krok a LocationID jako jeho atribut.deklarovat výchozí obor názvů elementu se používá v prologu XQuery tak, aby se všechny deklarace oboru názvů ve výsledném XML zobrazovaly v elementu nejvyšší úrovně. Výsledek tak bude čitelnější. Další informace o výchozích oborech názvů naleznete v tématu Zpracování oborů názvů v XQuery.
Toto je částečný výsledek:
<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>
...
Použití klauzule order by
Řazení v XQuery se provádí pomocí klauzule order by
ve výrazu FLWOR. Výrazy řazení předané klauzuli order by
musí vracet hodnoty, jejichž typy jsou platné pro operátor gt. Každý výraz řazení musí mít za následek jednu sekvenci s jednou položkou. Ve výchozím nastavení se řazení provádí ve vzestupném pořadí. Volitelně můžete pro každý výraz řazení zadat vzestupné nebo sestupné pořadí.
Poznámka
Porovnání řazení řetězcových hodnot provedených implementací XQuery v SQL Serveru se vždy provádí pomocí binární kolace kódu Unicode.
Následující dotaz načte všechna telefonní čísla pro konkrétního zákazníka ze sloupce AdditionalContactInfo. Výsledky jsou seřazené podle telefonního čísla.
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;
Všimněte si, že proces Atomization (XQuery) načte atomické hodnoty elementů <number
> před jeho předáním do order by
. Výraz můžete napsat pomocí funkce data(), ale to není povinné.
order by data($a/act:number[1]) descending
Toto je výsledek:
<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>
Místo deklarování oborů názvů v prologu dotazu je můžete deklarovat pomocí PŘÍKAZU 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;
Můžete také řadit podle hodnoty atributu. Následující dotaz například načte nově vytvořené prvky <Location
>, které mají atributy LocationID a LaborHours seřazené podle atributu LaborHours v sestupném pořadí. V důsledku toho se jako první vrátí umístění pracovních center s maximálním počtem pracovních hodin.
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;
Toto je výsledek:
<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"/>
V následujícím dotazu jsou výsledky seřazené podle názvu elementu. Dotaz načte specifikace konkrétního produktu z katalogu produktů. Specifikace jsou podřízené elementu <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;
Všimněte si následujících věcí z předchozího dotazu:
Výraz
/p1:ProductDescription/p1:Specifications/*
vrátí podřízené prvky <Specifications
>.Výraz
order by (local-name($a))
seřadí sekvenci podle místní části názvu elementu.
Toto je výsledek:
<Color>Available in most colors</Color>
<Material>Aluminum Alloy</Material>
<ProductLine>Mountain bike</ProductLine>
<RiderExperience>Advanced to Professional riders</RiderExperience>
<Style>Unisex</Style>
Uzly, ve kterých výraz řazení vrací prázdné, jsou seřazeny na začátek sekvence, jak je znázorněno v následujícím příkladu:
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
')
Toto je výsledek:
<Person />
<Person Name="A" />
<Person Name="B" />
Můžete zadat více kritérií řazení, jak je znázorněno v následujícím příkladu. Dotaz v tomto příkladu seřadí <Employee
> elementy nejprve podle názvu a potom podle hodnot atributů Administrator.
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
')
Toto je výsledek:
<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" />
Omezení implementace
Toto jsou omezení:
Výrazy řazení musí být homogenní. To je staticky zaškrtnuté.
Řazení prázdných sekvencí nelze řídit.
Nejprázdnší, prázdná největší a kolační klíčová slova v
order by
se nepodporují.