Istruzione e iterazione FLWOR (XQuery)
Si applica a: SQL Server
XQuery definisce la sintassi di iterazione delle espressioni FLWOR. FLWOR è l'acronimo di for
, let
, where
, order by
e return
.
Un'istruzione FLWOR è costituita dalle parti seguenti:
Una o più clausole FOR che associano una o più variabili iteratore alle sequenze di input.
Le sequenze di input possono essere altre espressioni XQuery, ad esempio espressioni XPath, e corrispondono a sequenze di nodi o a sequenze di valori atomici. È possibile costruire sequenze di valori atomici utilizzando valori letterali o funzioni costruttore. I nodi XML costruiti non sono consentiti come sequenze di input in SQL Server.
Una clausola
let
facoltativa. che assegna un valore alla variabile specificata per un'iterazione specifica. L'espressione assegnata può essere un'espressione XQuery, quale un'espressione XPath, e può restituire una sequenza di nodi o una sequenza di valori atomici. È possibile costruire sequenze di valori atomici utilizzando valori letterali o funzioni costruttore. I nodi XML costruiti non sono consentiti come sequenze di input in SQL Server.Una variabile iteratore. È possibile includere un'asserzione facoltativa del tipo in tale variabile tramite la parola chiave
as
.Una clausola
where
facoltativa. Tale clausola applica un predicato di filtro all'iterazione.Una clausola
order by
facoltativa.Un'espressione
return
. L'espressione nella clausolareturn
costruisce il risultato dell'istruzione FLWOR.
Ad esempio, la query seguente esegue l'iterazione degli <Step
> elementi nella prima posizione di produzione e restituisce il valore stringa dei <Step
> nodi:
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)
')
Risultato:
Manu step 1 at Loc 1 Manu step 2 at Loc 1 Manu step 3 at Loc 1
La query seguente è analoga alla precedente, tranne per il fatto che viene eseguita sulla colonna XML tipizzata Instructions della tabella ProductModel. La query esegue l'iterazione su tutti i passaggi di produzione, <step
> gli elementi, nella prima posizione del centro di lavoro per un prodotto specifico.
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
Dalla query precedente si noti quanto segue:
$Step
è la variabile iteratore.L'espressione di percorso,
//AWMI:root/AWMI:Location[1]/AWMI:step
, genera la sequenza di input. Questa sequenza è la sequenza degli <step
> elementi figlio del primo <Location
> nodo elemento.Non viene utilizzata la clausola del predicato facoltativa
where
.L'espressione
return
restituisce un valore stringa dall'elementostep
<> .
La funzione stringa (XQuery) viene usata per recuperare il valore stringa del <step
> nodo.
Risultato parziale:
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. ....
Negli esempi seguenti vengono illustrate sequenze di input aggiuntive consentite:
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
In SQL Server non sono consentite sequenze eterogenee. In particolare, non sono consentite le sequenze che contengono una combinazione di valori atomici e nodi.
L'iterazione viene spesso usata insieme alla sintassi XML Construction per trasformare i formati XML, come illustrato nella query successiva.
Nel database di esempio AdventureWorks le istruzioni di produzione archiviate nella colonna Instructions della tabella Production.ProductModel hanno il formato seguente:
<Location LocationID="10" LaborHours="1.2"
SetupHours=".2" MachineHours=".1">
<step>describes 1st manu step</step>
<step>describes 2nd manu step</step>
...
</Location>
...
La query seguente costruisce un nuovo codice XML con gli elementi con gli <Location
> attributi della posizione del centro di lavoro restituiti come elementi figlio:
<Location>
<LocationID>10</LocationID>
<LaborHours>1.2</LaborHours>
<SetupHours>.2</SetupHours>
<MachineHours>.1</MachineHours>
</Location>
...
Query:
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
Dalla query precedente si noti quanto segue:
L'istruzione FLWOR recupera una sequenza di <
Location
> elementi per un prodotto specifico.La funzione dati (XQuery) viene usata per estrarre il valore di ogni attributo in modo che vengano aggiunti al codice XML risultante come nodi di testo anziché come attributi.
L'espressione nella clausola RETURN costruisce la struttura XML desiderata.
Risultato parziale:
<Location>
<LocationID>10</LocationID>
<LaborHours>2.5</LaborHours>
<SetupHours>0.5</SetupHours>
<MachineHours>3</MachineHours>
</Location>
<Location>
...
<Location>
...
Utilizzo della clausola LET
È possibile utilizzare la clausola let
per denominare espressioni ripetute a cui è possibile fare riferimento tramite la variabile. L'espressione assegnata a una variabile let
viene inserita nella query ogni volta che viene fatto riferimento alla variabile nella query. Ciò significa che l'istruzione viene eseguita tante volte quante volte viene fatto riferimento all'espressione.
Nel database AdventureWorks2022
, le istruzioni di produzione contengono informazioni sugli strumenti richiesti e sulla posizione in cui vengono utilizzati. Nella query seguente viene utilizzata la clausola let
per elencare gli strumenti richiesti per la compilazione di un modello di produzione e i percorsi in cui è necessario ogni strumento.
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
Utilizzo della clausola where
È possibile usare la where
clausola per filtrare i risultati di un'iterazione. Questa operazione viene illustrata nell'esempio seguente.
Il processo di produzione di una bicicletta passa attraverso una serie di centri di lavorazione, ognuno dei quali definisce una sequenza di fasi di produzione. La query seguente recupera solo i centri di lavorazione che producono un modello di bicicletta e prevedono un numero di fasi di produzione inferiore a tre, Ovvero, hanno meno di tre <step
> elementi.
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
Dalla query precedente si noti quanto segue:
La
where
parola chiave usa la funzione count() per contare il numero di <step
> elementi figlio in ogni posizione del centro di lavoro.L'espressione
return
costruisce la struttura XML desiderata a partire dai risultati dell'iterazione.
Risultato:
<Location LocationID="30"/>
Il risultato dell'espressione nella clausola where
viene convertito in valore booleano tramite le regole seguenti, nell'ordine specificato. Si tratta delle stesse regole utilizzate per i predicati nelle espressioni di percorso, tranne per il fatto che non sono consentiti valori interi:
Se l'espressione
where
restituisce una sequenza vuota, il valore booleano effettivo è False.Se l'espressione
where
restituisce un valore di tipo booleano semplice, quest'ultimo è il valore booleano effettivo.Se l'espressione
where
restituisce una sequenza che contiene almeno un nodo, il valore booleano effettivo è True.In caso contrario, viene restituito un errore statico.
Associazione di più variabili in FLWOR
È possibile disporre di una singola espressione FLWOR che associa più variabili alle sequenze di input. Nell'esempio seguente, la query viene specificata su una variabile XML non tipizzata. L'espressione FLOWR restituisce il primo <Step
> elemento figlio in ogni <Location
> elemento.
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)
')
Dalla query precedente si noti quanto segue:
L'espressione
for
definisce$Loc
e $FirstStep
variables.Le
two
espressioni/ManuInstructions/Location
e$FirstStep in $Loc/Step[1]
, sono correlate in quanto i valori di$FirstStep
dipendono dai valori di$Loc
.L'espressione associata a
$Loc
genera una sequenza di <Location
> elementi. Per ogni <Location
> elemento,$FirstStep
genera una sequenza di un <Step
> elemento, un singleton.$Loc
viene specificata nell'espressione associata alla variabile$FirstStep
.
Risultato:
Manu step 1 at Loc 1
Manu step 1 at Loc 2
La query seguente è simile, ad eccezione del fatto che viene specificata per la colonna Instructions, la colonna xml tipizzata, della tabella ProductModel. La costruzione XML (XQuery) viene utilizzata per generare il codice XML desiderato.
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
Dalla query precedente si noti quanto segue:
La clausola
for
definisce due variabili,$WC
e$S
. L'espressione associata a$WC
genera una sequenza di centri di lavorazione per la produzione di un modello di bicicletta. L'espressione di percorso assegnata alla variabile$S
genera una sequenza di fasi per ogni sequenza di centri di lavorazione nella variabile$WC
.L'istruzione return costruisce xml con un <
Step
> elemento contenente il passaggio di produzione e LocationID come attributo.Lo spazio dei nomi dichiarare l'elemento predefinito viene usato nel prologo XQuery in modo che tutte le dichiarazioni dello spazio dei nomi nel codice XML risultante vengano visualizzate nell'elemento di primo livello. rendendo in tal modo più leggibile il risultato. Per altre informazioni sugli spazi dei nomi predefiniti, vedere Gestione degli spazi dei nomi in XQuery.
Risultato parziale:
<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>
...
Utilizzo della clausola order by
In XQuery, l'ordinamento viene eseguito utilizzando la clausola order by
nell'espressione FLWOR. Le espressioni di ordinamento passate alla order by
clausola devono restituire valori i cui tipi sono validi per l'operatore gt . Ogni espressione di ordinamento deve restituire un singleton, ovvero una sequenza costituita da un singolo elemento. Per impostazione predefinita, l'ordinamento viene eseguito in ordine crescente. Facoltativamente, è possibile specificare un ordine crescente o decrescente per ogni espressione di ordinamento.
Nota
L'ordinamento dei confronti sui valori stringa eseguiti dall'implementazione di XQuery in SQL Server viene sempre eseguito usando le regole di confronto del punto di codice Unicode binario.
La query seguente recupera tutti i numeri di telefono di un cliente specifico dalla colonna AdditionalContactInfo. I risultati vengono ordinati in base al numero di telefono.
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;
Si noti che il processo di atomizzazione (XQuery) recupera il valore atomico degli> <number
elementi prima di passarlo a .order by
È possibile scrivere l'espressione usando la funzione data(), ma non è necessaria.
order by data($a/act:number[1]) descending
Risultato:
<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>
Anziché dichiarare gli spazi dei nomi nel prologo della query, è possibile dichiararli tramite 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;
È inoltre possibile eseguire l'ordinamento in base al valore di attributo. Ad esempio, la query seguente recupera gli elementi appena creati <Location
> con gli attributi LocationID e LaborHours ordinati in base all'attributo LaborHours in ordine decrescente. Di conseguenza, vengono restituiti per primi i centri di lavorazione con il numero massimo di ore di manodopera.
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;
Risultato:
<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"/>
Nella query seguente, i risultati vengono ordinati in base al nome dell'elemento. La query recupera le specifiche di un determinato prodotto dal catalogo prodotti. Le specifiche sono gli elementi figlio dell'elemento <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;
Dalla query precedente si noti quanto segue:
L'espressione
/p1:ProductDescription/p1:Specifications/*
restituisce elementi figlio diSpecifications
<> .L'espressione
order by (local-name($a))
ordina la sequenza in base alla parte locale del nome dell'elemento.
Risultato:
<Color>Available in most colors</Color>
<Material>Aluminum Alloy</Material>
<ProductLine>Mountain bike</ProductLine>
<RiderExperience>Advanced to Professional riders</RiderExperience>
<Style>Unisex</Style>
I nodi in cui l'espressione di ordinamento restituisce una sequenza vuota vengono inseriti all'inizio della sequenza, come illustrato nell'esempio seguente:
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
')
Risultato:
<Person />
<Person Name="A" />
<Person Name="B" />
È possibile specificare più criteri di ordinamento, come illustrato nell'esempio seguente La query in questo esempio ordina prima gli <Employee
> elementi in base a Title e quindi in base ai valori dell'attributo 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
')
Risultato:
<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" />
Limitazioni di implementazione
Limitazioni:
Le espressioni di ordinamento devono essere tipizzate in modo omogeneo. Per verificarlo, viene eseguito il controllo statico.
Non è possibile controllare l'ordinamento di sequenze vuote.
Non sono supportate le parole chiave empty least, empty greatest e collation nella clausola
order by
.