Compartilhar via


Construção XML (XQuery)

Aplica-se: SQL Server

No XQuery, você pode usar os construtores diretos e computados para construir estruturas XML em uma consulta.

Observação

Não há diferença entre os construtores diretos e computados .

Usando construtores diretos

Ao usar construtores diretos, você especifica a sintaxe semelhante a XML ao construir o XML. Os exemplos a seguir ilustram a construção XML pelos construtores diretos.

Construindo elementos

Usando notações de XML, é possível construir elementos. O exemplo a seguir usa a expressão do construtor de elemento direto e cria um <elemento ProductModel> . O elemento construído tem três elementos filho

  • Um nó de texto.

  • Dois nós de elemento, <Resumo> e <Recursos>.

    • O <elemento Summary> tem um filho de nó de texto cujo valor é "Alguma descrição".

    • O <elemento Recursos> tem três filhos de nó de elemento, <Cor>, <Peso> e <Garantia>. Cada um desses nós tem um nó de texto filho e os valores Red, 25, 2 years parts and labor, respectivamente.

declare @x xml;  
set @x='';  
select @x.query('<ProductModel ProductModelID="111">;  
This is product model catalog description.  
<Summary>Some description</Summary>  
<Features>  
  <Color>Red</Color>  
  <Weight>25</Weight>  
  <Warranty>2 years parts and labor</Warranty>  
</Features></ProductModel>')  
  

Este é o XML resultante:

<ProductModel ProductModelID="111">  
  This is product model catalog description.  
  <Summary>Some description</Summary>  
  <Features>  
    <Color>Red</Color>  
    <Weight>25</Weight>  
    <Warranty>2 years parts and labor</Warranty>  
  </Features>  
</ProductModel>  

Embora seja útil construir elementos de expressões constantes, como mostrado neste exemplo, o verdadeiro poder desse recurso de linguagem do XQuery é a capacidade de construir XML capaz de extrair dinamicamente dados de um banco de dados. Você pode usar chaves para especificar expressões de consulta. No XML resultante, a expressão é substituída por seu valor. Por exemplo, a consulta a seguir constrói um <NewRoot> elemento com um elemento filho (<e>). O valor de element <e> é calculado especificando uma expressão de caminho dentro de chaves ("{ ... }").

DECLARE @x xml;  
SET @x='<root>5</root>';  
SELECT @x.query('<NewRoot><e> { /root } </e></NewRoot>');  

As chaves agem como tokens de troca de contexto e alternam a consulta da construção XML para avaliação de consulta. Nesse caso, a expressão de caminho XQuery dentro das chaves, /root, é avaliada e os resultados são substituídos por ela.

Este é o resultado:

<NewRoot>  
  <e>  
    <root>5</root>  
  </e>  
</NewRoot>  

A consulta a seguir é semelhante à anterior. No entanto, a expressão nas chaves especifica a função data() para recuperar o valor atômico do>root< elemento e o atribui ao elemento construído, . <e>

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('  
                           <NewRoot>  
                             <e> { data(/root) } </e>  
                           </NewRoot>' ));  
SELECT @y;  

Este é o resultado:

<NewRoot>  
  <e>5</e>  
</NewRoot>  

Para usar as chaves como parte do texto em vez de tokens de troca de contexto, é possível não utilizá-las como "\} \} " ou "\{\{", como mostrado neste exemplo:

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('  
<NewRoot> Hello, I can use {{ and  }} as part of my text</NewRoot>'));  
SELECT @y;  

Este é o resultado:

<NewRoot> Hello, I can use { and  } as part of my text</NewRoot>  

A consulta a seguir é outro exemplo de construção de elementos com o uso do construtor de elemento direto. Além disso, o <FirstLocation> valor do elemento é obtido executando a expressão nas chaves. A expressão de consulta retorna as etapas de fabricação no primeiro local de centro de trabalho da coluna Instructions da tabela Production.ProductModel.

SELECT Instructions.query('  
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
        <FirstLocation>  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM Production.ProductModel  
WHERE ProductModelID=7;  

Este é o resultado:

<FirstLocation>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">  
      Insert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.   
  </AWMI:step>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">  
      Attach <AWMI:tool>Trim Jig TJ-26</AWMI:tool> to the upper and lower right corners of the aluminum sheet.   
  </AWMI:step>  
   ...  
</FirstLocation>  

Conteúdo de elemento na construção XML

O exemplo a seguir ilustra o comportamento das expressões na construção do conteúdo de elemento com o uso do construtor de elemento direto. No exemplo a seguir, o construtor de elemento direto especifica uma expressão. Para essa expressão, um nó de texto é criado no XML resultante.

declare @x xml;  
set @x='  
<root>  
  <step>This is step 1</step>  
  <step>This is step 2</step>  
  <step>This is step 3</step>  
</root>';  
select @x.query('  
<result>  
 { for $i in /root[1]/step  
    return string($i)  
 }  
</result>');  
  

A sequência de valor atômico resultante da avaliação de expressão é adicionada ao nó de texto com um espaço adicionado entre os valores atômicos adjacentes, como mostrado no resultado. O elemento construído tem um filho. Ele é um nó de texto que contém o valor mostrado no resultado.

<result>This is step 1 This is step 2 This is step 3</result>  

Em vez de uma expressão, se você especificar três expressões separadas que geram três nós de texto, os nós de texto adjacentes serão mesclados em um único nó de texto, por meio de concatenação, no XML resultante.

declare @x xml;  
set @x='  
<root>  
  <step>This is step 1</step>  
  <step>This is step 2</step>  
  <step>This is step 3</step>  
</root>';  
select @x.query('  
<result>  
 { string(/root[1]/step[1]) }  
 { string(/root[1]/step[2]) }  
 { string(/root[1]/step[3]) }  
</result>');  

O nó de elemento construído tem um filho. Ele é um nó de texto que contém o valor mostrado no resultado.

<result>This is step 1This is step 2This is step 3</result>  

Construindo atributos

Ao construir elementos usando o construtor de elemento direto, também é possível especificar atributos do elemento usando sintaxe similar a XML, como mostrado neste exemplo:

declare @x xml;  
set @x='';  
select @x.query('<ProductModel ProductModelID="111">;  
This is product model catalog description.  
<Summary>Some description</Summary>  
</ProductModel>')  

Este é o XML resultante:

<ProductModel ProductModelID="111">  
  This is product model catalog description.  
  <Summary>Some description</Summary>  
</ProductModel>  

O elemento <ProductModel> construído tem um atributo ProductModelID e estes nós filho:

  • Um nó de texto, This is product model catalog description.

  • Um nó de elemento, <Summary>. Esse nó tem um filho de nó de texto, Some description.

Ao construir um atributo, é possível especificar seu valor com uma expressão entre chaves. Nesse caso, o resultado da expressão é retornado como o valor de atributo.

No exemplo a seguir, a função data() não é estritamente necessária. Como você está atribuindo o valor da expressão a um atributo, data() é aplicado implicitamente para recuperar o valor digitado da expressão especificada.

DECLARE @x xml;  
SET @x='<root>5</root>';  
DECLARE @y xml;  
SET @y = (SELECT @x.query('<NewRoot attr="{ data(/root) }" ></NewRoot>'));  
SELECT @y;  

Este é o resultado:

<NewRoot attr="5" />  

A seguir, um outro exemplo no qual são especificadas expressões para a construção de atributo LocationID e SetupHrs. Essas expressões são avaliadas com base no XML na coluna Instruction. O valor digitado da expressão é atribuído aos atributos.

SELECT Instructions.query('  
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
        <FirstLocation   
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7;  

Este é o resultado parcial:

<FirstLocation LocationID="10" SetupHours="0.5" >  
  <AWMI:step ...   
  </AWMI:step>  
  ...  
</FirstLocation>  

Limitações de implementação

Estas são as limitações:

  • Não há suporte a expressões de atributo múltiplas ou mistas (cadeia de caracteres e expressão XQuery). Por exemplo, como mostrado na consulta a seguir, você constrói um XML em que Item é uma constante e o valor 5 é obtido avaliando uma expressão de consulta:

    <a attr="Item 5" />  
    

    A consulta a seguir retorna um erro, pois você está misturando cadeia de caracteres constante com uma expressão ({/x}) e não há suporte para isso:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    SELECT @x.query( '<a attr="Item {/x}"/>' )   
    

    Nesse caso, você tem as seguintes opções:

    • Forme o valor de atributo pela concatenação de dois valores atômicos. Esses valores atômicos são serializados no valor de atributo com um espaço entre os valores atômicos:

      SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )   
      

      Este é o resultado:

      <a attr="Item 5" />  
      
    • Use a função concat para concatenar os dois argumentos de cadeia de caracteres no valor do atributo resultante:

      SELECT @x.query( '<a attr="{concat(''Item'', /x[1])}"/>' )   
      

      Nesse caso, não há espaço adicionado entre os dois valores de cadeia de caracteres. Se quiser um espaço entre os dois valores, insira-o explicitamente.

      Este é o resultado:

      <a attr="Item5" />  
      
  • Não há suporte para expressões múltiplas como um valor de atributo. Por exemplo, a consulta a seguir retorna um erro:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    SELECT @x.query( '<a attr="{/x}{/x}"/>' )  
    
  • Não há suporte para sequências heterogêneas. Qualquer tentativa para atribuir uma sequência heterogênea como um valor de atributo retornará um erro, como mostrado no exemplo a seguir. Neste exemplo, uma sequência heterogênea, uma string "Item" e um elemento <x>, é especificado como o valor do atributo:

    DECLARE @x xml  
    SET @x ='<x>5</x>'  
    select @x.query( '<a attr="{''Item'', /x }" />')  
    

    Se você aplicar a função data(), a consulta funcionará porque recupera o valor atômico da expressão, /x, que é concatenado com a string. Apresentamos a seguir uma sequência de valores atômicos:

    SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )   
    

    Este é o resultado:

    <a attr="Item 5" />  
    
  • A ordem de nó do atributo é aplicada durante a serialização e não durante a verificação de tipo estático. Por exemplo, a consulta a seguir falha, pois tenta adicionar um atributo depois de um nó sem-atributo.

    select convert(xml, '').query('  
    element x { attribute att { "pass" }, element y { "Element text" }, attribute att2 { "fail" } }  
    ')  
    go  
    

    A consulta anterior retorna o seguinte erro:

    XML well-formedness check: Attribute cannot appear outside of element declaration. Rewrite your XQuery so it returns well-formed XML.  
    

Adicionando namespaces

Ao construir XML usando os construtores diretos, o elemento construído e os nomes de atributo podem ser qualificados com o uso de um prefixo de namespace. É possível associar o prefixo ao namespace das seguintes formas:

  • Usando um atributo de declaração de namespace.

  • Usando a cláusula WITH XMLNAMESPACES.

  • No prólogo do XQuery.

Usando um atributo de declaração de namespace para adicionar namespaces

O exemplo a seguir usa um atributo de declaração de namespace na construção do elemento <a> para declarar um namespace padrão. A construção do elemento <b> filho desfaz a declaração do namespace padrão declarado no elemento pai.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <a xmlns="a">  
    <b xmlns=""/>  
  </a>' )   

Este é o resultado:

<a xmlns="a">  
  <b xmlns="" />  
</a>  

É possível atribuir um prefixo ao namespace. O prefixo é especificado na construção do elemento <a>.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <x:a xmlns:x="a">  
    <b/>  
  </x:a>' )  

Este é o resultado:

<x:a xmlns:x="a">  
  <b />  
</x:a>  

É possível desfazer a declaração de um namespace padrão na construção XML, mas não desfazer a declaração de um prefixo de namespace. A consulta a seguir retorna um erro, pois não é possível cancelar a declaração de um prefixo conforme especificado na construção do elemento <b>.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
  <x:a xmlns:x="a">  
    <b xmlns:x=""/>  
  </x:a>' )  

O namespace recentemente construído está disponível para uso na consulta. Por exemplo, a consulta a seguir declara um namespace na construção do elemento, <FirstLocation>e especifica o prefixo nas expressões para os valores de atributo LocationID e SetupHrs.

SELECT Instructions.query('  
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"  
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Observe que a criação de um novo prefixo de namespace desse modo substituirá qualquer declaração preexistente de namespace desse prefixo. Por exemplo, a declaração de namespace, AWMI="https://someURI", no prólogo de consulta é substituída pela declaração de namespace no <FirstLocation> elemento.

SELECT Instructions.query('  
declare namespace AWMI="https://someURI";  
        <FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"  
         LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
         SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
           { /AWMI:root/AWMI:Location[1]/AWMI:step }  
        </FirstLocation>   
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Usando um prólogo para adicionar namespaces

Este exemplo ilustra como podem ser adicionados namespaces ao XML construído. Um namespace padrão é declarado no prólogo de consulta.

declare @x xml  
set @x ='<x>5</x>'  
select @x.query( '  
           declare default element namespace "a";  
            <a><b xmlns=""/></a>' )  

Observe que, na construção do elemento <b>, o atributo de declaração de namespace é especificado com uma string vazia como seu valor. Isso desfaz a declaração do namespace padrão declarado no pai.

Este é o resultado:

<a xmlns="a">  
  <b xmlns="" />  
</a>  

Tratamento de espaços em branco e construção XML

O conteúdo de elemento na construção XML pode incluir caracteres de espaço em branco. Esses caracteres são tratados das seguintes formas:

  • Os caracteres de espaço em branco em URIs de namespace são tratados como o tipo XSD anyURI. Especificamente, eles são tratados da seguinte forma:

    • Qualquer caractere com espaço em branco no início e término é eliminado.

    • Os valores de caractere com espaço em branco são reduzidos para um único espaço.

  • Os caracteres de avanço de linha no conteúdo de atributo são substituídos por espaços. Todos os outros caracteres com espaço em branco permanecem inalterados.

  • O espaço em branco nos elementos é preservado.

O exemplo a seguir ilustra o tratamento de espaço em branco na construção XML:

-- line feed is replaced by space.  
declare @x xml  
set @x=''  
select @x.query('  
  
declare namespace myNS="   https://       
 abc/  
xyz  
  
";  
<test attr="    my   
test   attr   
value    " >  
  
<a>  
  
This     is  a  
  
test  
  
</a>  
</test>  
') as XML_Result  
  

Este é o resultado:

-- result  
<test attr="<test attr="    my test   attr  value    "><a>  
  
This     is  a  
  
test  
  
</a></test>  
"><a>  
  
This     is  a  
  
test  
  
</a></test>  

Outros construtores XML diretos

Os construtores de processamento de instruções e comentários XML usam a mesma sintaxe do construtor XML correspondente. Também há suporte para construtores computados para nós de texto, mas eles são usados principalmente em XML DML para construir nós de texto.

Observação Para obter um exemplo de como usar um construtor de nó de texto explícito, consulte o exemplo específico em inserir (XML DML).

Na consulta a seguir, o XML construído inclui um elemento, dois atributos, um comentário e uma instrução de processamento. Observe que uma vírgula é usada antes do <FirstLocation>, porque uma sequência está sendo construída.

SELECT Instructions.query('  
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
   <?myProcessingInstr abc="value" ?>,   
   <FirstLocation   
        WorkCtrID = "{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"  
        SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >  
       <!-- some comment -->  
       <?myPI some processing instructions ?>  
       { (/AWMI:root/AWMI:Location[1]/AWMI:step) }  
    </FirstLocation>   
') as Result   
FROM Production.ProductModel  
where ProductModelID=7;  
  

Este é o resultado parcial:

<?myProcessingInstr abc="value" ?>  
<FirstLocation WorkCtrID="10" SetupHrs="0.5">  
  <!-- some comment -->  
  <?myPI some processing instructions ?>  
  <AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">I  
  nsert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.   
  </AWMI:step>  
    ...  
</FirstLocation>  
  

Usando construtores computados

. Nesse caso, especifique as palavras-chave que identificam o tipo de nó que você deseja construir. São suportadas apenas as seguintes palavras-chave:

  • element

  • atributo

  • text

Para elemento e nós de atributo, essas palavras-chave são seguidas pelo nome de nó e também pela expressão, entre chaves, que gera o conteúdo para esse nó. No exemplo a seguir, você está construindo este XML:

<root>  
  <ProductModel PID="5">Some text <summary>Some Summary</summary></ProductModel>  
</root>  

Esta é a consulta que usa construtores computados para gerar o XML:

declare @x xml  
set @x=''  
select @x.query('element root   
               {   
                  element ProductModel  
     {  
attribute PID { 5 },  
text{"Some text "},  
    element summary { "Some Summary" }  
 }  
               } ')  
  

A expressão que gera o conteúdo de nó pode especificar uma expressão de consulta.

declare @x xml  
set @x='<a attr="5"><b>some summary</b></a>'  
select @x.query('element root   
               {   
                  element ProductModel  
     {  
attribute PID { /a/@attr },  
text{"Some text "},  
    element summary { /a/b }  
 }  
               } ')  

Observe que o elemento computado e os construtores de atributo, como definidos na especificação de XQuery, permitem computar os nomes de nó. Quando você estiver usando construtores diretos no SQL Server, os nomes de nó, como elemento e atributo, devem ser especificados como literais constantes. Portanto, não há diferença entre construtores diretos e construtores computados para elementos e atributos.

No exemplo a seguir, o conteúdo dos nós construídos é obtido das instruções de fabricação XML armazenadas na coluna Instruções do tipo de dados xml na tabela ProductModel.

SELECT Instructions.query('  
  declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";  
   element FirstLocation   
     {  
        attribute LocationID { (/AWMI:root/AWMI:Location[1]/@LocationID)[1] },  
        element   AllTheSteps { /AWMI:root/AWMI:Location[1]/AWMI:step }  
     }  
') as Result   
FROM  Production.ProductModel  
where ProductModelID=7  

Este é o resultado parcial:

<FirstLocation LocationID="10">  
  <AllTheSteps>  
    <AWMI:step> ... </AWMI:step>  
    <AWMI:step> ... </AWMI:step>  
    ...  
  </AllTheSteps>  
</FirstLocation>    

Limitações adicionais de implementação

Não podem ser usados construtores de atributo computados para declarar um novo namespace. Além disso, não há suporte para os seguintes construtores calculados no SQL Server:

  • Construtores de nó de documento computados

  • Construtores de instrução de processamento computados

  • Construtores de comentário computados

Confira também

Expressões XQuery