Sdílet prostřednictvím


Příkaz FLWOR a iterace (XQuery)

platí pro:SQL Server

XQuery definuje syntaxi iterace FLWOR. FLWOR je zkratka pro for, let, where, order bya 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 klauzuli return 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á:

  1. Pokud výraz where vrátí prázdnou sekvenci, jeho efektivní logická hodnota je False.

  2. Pokud výraz where vrátí jednu jednoduchou logickou hodnotu typu, jedná se o efektivní logickou hodnotu.

  3. Pokud výraz where vrátí sekvenci, která obsahuje alespoň jeden uzel, efektivní logická hodnota je True.

  4. 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í.

Viz také

výrazy XQuery