Partilhar via


Instrução e iteração FLWOR (XQuery)

Aplica-se a:SQL Server

XQuery define a sintaxe de iteração FLWR. FLWOR é o acrónimo de for, let, where, order by, e return.

Uma declaração FLWOR é composta pelas seguintes partes:

  • Uma ou mais FOR cláusulas que ligam uma ou mais variáveis iteradoras a sequências de entrada.

    As sequências de entrada podem ser outras expressões XQuery, como expressões XPath. São sequências de nós ou sequências de valores atómicos. Sequências de valores atômicos podem ser construídas usando literais ou funções de construtor. Os nós XML construídos não são permitidos como sequências de entrada no SQL Server.

  • Cláusula opcional let . Esta cláusula atribui um valor à variável dada para uma iteração específica. A expressão atribuída pode ser uma expressão XQuery, como uma expressão XPath, e pode retornar uma sequência de nós ou uma sequência de valores atômicos. Sequências de valores atômicos podem ser construídas usando literais ou funções de construtor. Os nós XML construídos não são permitidos como sequências de entrada no SQL Server.

  • Uma variável iteradora. Esta variável pode ter uma asserção de tipo opcional usando a as palavra-chave.

  • Cláusula opcional where . Esta cláusula aplica um predicado de filtro na iteração.

  • Cláusula opcional order by .

  • Uma return expressão. A expressão na return cláusula constrói o resultado da instrução FLWOR.

Por exemplo, a consulta a seguir itera sobre os <Step> elementos no primeiro local de fabricação e retorna o valor da cadeia de caracteres dos <Step> nós:

DECLARE @x AS 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)
');

Eis o resultado:

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

A consulta a seguir é semelhante à anterior, exceto que é especificada na coluna Instruções, uma coluna xml digitada, da tabela ProductModel. A consulta itera todas as etapas de fabricação, <step> elementos, no primeiro local do centro de trabalho para um produto específico.

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;

Observe o seguinte da consulta anterior:

  • A $Step é a variável iteradora.

  • A expressão de caminho, //AWMI:root/AWMI:Location[1]/AWMI:step, gera a sequência de entrada. Esta sequência é a sequência dos <step> filhos do nó do elemento do primeiro <Location> nó do elemento.

  • A cláusula de predicado opcional, where, não é usada.

  • A return expressão retorna um valor de cadeia de caracteres do <step> elemento .

A função string (XQuery) é usada para recuperar o valor da string do <step> nó.

Eis o resultado parcial:

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

Estes são exemplos de outras sequências de entrada permitidas:

DECLARE @x AS XML;

SET @x = '';

SELECT @x.query('
for $a in (1, 2, 3)
  return $a');
-- result = 1 2 3

DECLARE @x AS 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 AS 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

No SQL Server, sequências heterogêneas não são permitidas. Especificamente, sequências que contêm uma mistura de valores atômicos e nós não são permitidas.

A iteração é freqüentemente usada junto com a sintaxe de construção XML (XQuery) na transformação de formatos XML, conforme mostrado na próxima consulta.

No banco de dados de exemplo AdventureWorks, as instruções de fabricação armazenadas na Instructions coluna da tabela têm a Production.ProductModel seguinte forma:

<Location LocationID="10" LaborHours="1.2"
            SetupHours=".2" MachineHours=".1">
  <step>describes 1st manu step</step>
   <step>describes 2nd manu step</step>
   ...
</Location>
...

A consulta a seguir constrói um novo XML que tem os <Location> elementos com os atributos de local do centro de trabalho retornados como elementos filho:

<Location>
   <LocationID>10</LocationID>
   <LaborHours>1.2</LaborHours>
   <SetupHours>.2</SetupHours>
   <MachineHours>.1</MachineHours>
</Location>
...

Aqui está a consulta:

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;

Observe as seguintes considerações da consulta anterior:

  • A instrução FLWOR recupera uma sequência de <Location> elementos para um produto específico.

  • A função de dados (XQuery) é usada para extrair o valor de cada atributo para que eles sejam adicionados ao XML resultante como nós de texto em vez de atributos.

  • A expressão na RETURN cláusula constrói o XML desejado.

Este é um resultado parcial:

<Location>
  <LocationID>10</LocationID>
  <LaborHours>2.5</LaborHours>
  <SetupHours>0.5</SetupHours>
  <MachineHours>3</MachineHours>
</Location>
<Location>
   ...
<Location>
...

Utilizar a let cláusula

Você pode usar a let cláusula para nomear expressões repetitivas às quais você pode se referir referindo-se à variável. A expressão atribuída a uma let variável é inserida na consulta sempre que a variável é referenciada na consulta. Isso significa que a instrução é executada tantas vezes quanto a expressão é referenciada.

No banco de dados, as AdventureWorks2025 instruções de fabricação contêm informações sobre as ferramentas necessárias e o local onde as ferramentas são usadas. A consulta a seguir usa a let cláusula para listar as ferramentas necessárias para criar um modelo de produção e os locais onde cada ferramenta é necessária.

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;

Utilizar a where cláusula

Você pode usar a cláusula para filtrar os where resultados de uma iteração. Isto é ilustrado neste próximo exemplo.

Na fabricação de uma bicicleta, o processo de fabricação passa por uma série de locais de centros de trabalho. Cada local do centro de trabalho define uma sequência de etapas de fabricação. A consulta a seguir recupera apenas os locais do centro de trabalho que fabricam um modelo de bicicleta e têm menos de três etapas de fabricação. Ou seja, têm menos de três <step> elementos.

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;

Observe o seguinte na consulta anterior:

  • A where palavra-chave usa a count() função para contar o número de elementos filho em cada local do centro de <step> trabalho.

  • A return expressão constrói o XML desejado a partir dos resultados da iteração.

Eis o resultado:

<Location LocationID="30"/>

O resultado da expressão na cláusula é convertido em where um valor booleano usando as seguintes regras, na ordem especificada. Estas são as mesmas regras para predicados em expressões de caminho, exceto que inteiros não são permitidos:

  1. Se a where expressão retornar uma sequência vazia, seu valor booleano efetivo será False.

  2. Se a where expressão retornar um valor de tipo booleano simples, esse valor será o valor booleano efetivo.

  3. Se a where expressão retornar uma sequência que contém pelo menos um nó, o valor booleano efetivo será True.

  4. Caso contrário, gera um erro estático.

Ligação de múltiplas variáveis no FLWOR

Você pode ter uma única expressão FLWOR que vincula várias variáveis a sequências de entrada. No exemplo a seguir, a consulta é especificada em relação a uma variável xml sem tipo. A expressão FLOWR retorna o primeiro <Step> elemento filho em cada <Location> elemento.

DECLARE @x AS 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)
');

Observe o seguinte da consulta anterior:

  • A for expressão define $Loc e $FirstStep variáveis.

  • As two expressões, /ManuInstructions/Location e $FirstStep in $Loc/Step[1], estão correlacionadas na medida em que os valores de dependem dos valores de $FirstStep$Loc.

  • A expressão associada gera $Loc uma sequência de <Location> elementos. Para cada <Location> elemento, $FirstStep gera uma sequência de um <Step> elemento, um singleton.

  • $Loc é especificada na expressão associada à $FirstStep variável.

Eis o resultado:

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

A consulta a seguir é semelhante, exceto que é especificada na coluna Instruções, coluna xml digitada, da ProductModel tabela. A construção XML (XQuery) é usada para gerar o XML desejado.

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;

Observe o seguinte na consulta anterior:

  • A for cláusula define duas variáveis, $WC e $S. A expressão associada gera $WC uma sequência de locais de centros de trabalho na fabricação de um modelo de produto de bicicleta. A expressão de caminho atribuída à $S variável gera uma sequência de etapas para cada sequência de local do centro de trabalho no $WC.

  • A instrução return constrói XML que tem um <Step> elemento que contém a etapa de fabricação e o LocationID como seu atributo.

  • O namespace declare default element é usado no prolog XQuery para que todas as declarações de namespace no XML resultante apareçam no elemento de nível superior. Isso torna o resultado mais legível. Para obter mais informações sobre namespaces padrão, consulte Manipulando namespaces no XQuery.

Eis o resultado parcial:

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

Utilizar a order by cláusula

A classificação no XQuery é realizada usando a order by cláusula na expressão FLWOR. As expressões de classificação passadas para a order by cláusula devem retornar valores cujos tipos são válidos para o gt operador. Cada expressão de classificação deve resultar em uma sequência singleton com um item. Por padrão, a classificação é realizada em ordem crescente. Opcionalmente, você pode especificar ordem crescente ou decrescente para cada expressão de classificação.

Observação

As comparações de classificação em valores de cadeia de caracteres executadas pela implementação XQuery no SQL Server são sempre realizadas usando o agrupamento de pontos de código binários Unicode.

A consulta a seguir recupera todos os números de telefone de um cliente específico na coluna AdditionalContactInfo. Os resultados são ordenados por número de telefone.

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;

O processo de atomização (XQuery) recupera o <number> valor atômico dos elementos antes de passá-lo para .order by Você pode escrever a expressão usando a data() função, mas isso não é necessário.

order by data($a/act:number[1]) descending

Eis o resultado:

<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>

Em vez de declarar os namespaces no prólogo de consulta, você pode declará-los usando 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;

Você também pode classificar por valor de atributo. Por exemplo, a consulta a seguir recupera os elementos recém-criados <Location> que têm os atributos LocationID e LaborHours classificados pelo atributo LaborHours em ordem decrescente. Como resultado, os locais do centro de trabalho que têm o máximo de horas de trabalho são devolvidos primeiro.

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;

Eis o resultado:

<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"/>

Na consulta a seguir, os resultados são classificados por nome do elemento. A consulta recupera as especificações de um produto específico do catálogo de produtos. As especificações são os filhos do <Specifications> elemento.

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;

Observe o seguinte da consulta anterior:

  • A /p1:ProductDescription/p1:Specifications/* expressão retorna o elemento filhos de<Specifications> .

  • A order by (local-name($a)) expressão classifica a sequência pela parte local do nome do elemento.

Eis o resultado:

<Color>Available in most colors</Color>
<Material>Aluminum Alloy</Material>
<ProductLine>Mountain bike</ProductLine>
<RiderExperience>Advanced to Professional riders</RiderExperience>
<Style>Unisex</Style>

Os nós nos quais a expressão de ordenação retorna vazia são classificados até o início da sequência, conforme mostrado no exemplo a seguir:

DECLARE @x AS 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
');

Eis o resultado:

<Person />
<Person Name="A" />
<Person Name="B" />

Você pode especificar vários critérios de classificação, conforme mostrado no exemplo a seguir. A consulta neste exemplo classifica os elementos primeiro por Título e, em seguida, por valores de <Employee> atributo Administrador.

DECLARE @x AS 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
');

Eis o resultado:

<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" />

Limitações

Estas são as limitações:

  • As expressões de classificação devem ser digitadas de forma homogênea. Isso é verificado estaticamente.

  • A classificação de sequências vazias não pode ser controlada.

  • Não há suporte para as palavras-chave vazio menos, vazio maior e agrupamento em order by