Partekatu bidez


Construcción XML (XQuery)

Se aplica a:SQL Server

En XQuery, puede usar los constructores directos y calculados para construir estructuras XML dentro de una consulta.

Nota:

No hay ninguna diferencia entre los constructores directos y calculados .

Uso de constructores directos

Cuando se utilizan constructores directos, se especifica una sintaxis parecida a XML al construir el XML. En los ejemplos siguientes se muestra la construcción de XML mediante los constructores directos.

Elementos de construcción

Cuando se utilizan notaciones XML, se pueden construir elementos. En el ejemplo siguiente se usa la expresión del constructor de elementos directos y se crea un <ProductModel> elemento . El elemento construido tiene tres elementos secundarios

  • Un nodo de texto.

  • Dos nodos de elemento y <Summary><Features>.

    • El <Summary> elemento tiene un elemento secundario de nodo de texto cuyo valor es "Some description".

    • El <Features> elemento tiene tres elementos secundarios de nodo de elemento, <Color>, <Weight>y <Warranty>. Cada uno de estos nodos tiene un elemento secundario de nodo de texto y tiene los valores Red, 25, 2 years parts and labor, respectivamente.

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

Aunque la construcción de elementos a partir de expresiones constantes, tal como se muestra en este ejemplo, resulta útil, la auténtica ventaja de esta característica del lenguaje XQuery es la capacidad de construir XML que extrae datos de una base de datos de forma dinámica. Puede utilizar llaves para especificar expresiones de consulta. En el XML resultante, se sustituye la expresión por su valor. Por ejemplo, la consulta siguiente construye un <NewRoot> elemento con un elemento secundario (<e>). El valor del elemento <e> se calcula especificando una expresión de ruta de acceso dentro de llaves ("{ ... }").

DECLARE @x AS XML;

SET @x = '<root>5</root>';

SELECT @x.query('<NewRoot><e> { /root } </e></NewRoot>');

Las llaves actúan como tokens de cambio de contexto y cambian la consulta de construcción de XML a evaluación de consulta. En este caso, se evalúa la expresión de ruta de acceso de XQuery entre llaves, /root, y se sustituyen los resultados por la misma.

Este es el resultado:

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

La consulta siguiente es similar a la anterior. Sin embargo, la expresión de las llaves especifica la data() función para recuperar el valor atómico del <root> elemento y lo asigna al elemento construido, <e>.

DECLARE @x AS XML;
SET @x = '<root>5</root>';

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

SELECT @y;

Este es el resultado:

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

Si desea utilizar llaves como parte del texto en lugar de como token de cambio de contexto, debe utilizarlas con valores de escape como "}}" o "{{", tal como se muestra en el ejemplo siguiente:

DECLARE @x AS XML;
SET @x = '<root>5</root>';

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

SELECT @y;

Este es el resultado:

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

La consulta siguiente es otro ejemplo de construcción de elementos mediante el constructor de elemento directo. Además, el valor del <FirstLocation> elemento se obtiene ejecutando la expresión en las llaves. La expresión de consulta devuelve los pasos de fabricación de la primera ubicación de centro de trabajo de la columna Instructions de la tabla 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 es el 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>

Contenido de elementos en la construcción XML

En el ejemplo siguiente se ilustra el comportamiento de las expresiones en la construcción de contenido de elemento mediante el constructor de elemento directo. En el ejemplo siguiente, el constructor de elemento directo especifica una expresión. Para esta expresión, se crea un nodo de texto en el XML resultante.

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

La secuencia de valores atómicos resultante de la evaluación de la expresión se agrega al nodo de texto con un espacio adicional entre los valores atómicos adyacentes, tal como se muestra en el resultado. El elemento construido tiene un elemento secundario. Se trata de un nodo de texto que incluye el valor que se muestra en el resultado.

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

Si en lugar de una expresión se especifican tres expresiones independientes que generan tres nodos de texto, los nodos de texto adyacentes se combinarán en uno solo, mediante concatenación, en el XML resultante.

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

El nodo del elemento construido tiene un elemento secundario. Se trata de un nodo de texto que incluye el valor que se muestra en el resultado.

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

Construir atributos

Al construir elementos mediante el constructor de elementos directos, también puede especificar atributos del elemento mediante la sintaxis similar a XML, como se muestra en este ejemplo:

DECLARE @x AS XML;
SET @x = '';

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

Este es el XML resultante:

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

El elemento <ProductModel> construido tiene un atributo ProductModelID y estos nodos secundarios:

  • Un nodo de texto, This is product model catalog description.

  • Un nodo de elemento, <Summary>. Este nodo tiene un nodo de texto secundario, Some description.

Al construir un atributo, puede especificar su valor con una expresión entre llaves. En este caso, el resultado de la expresión se devuelve como el valor del atributo.

En el ejemplo siguiente, la data() función no es estrictamente necesaria. Dado que va a asignar el valor de expresión a un atributo, data() se aplica implícitamente para recuperar el valor con tipo de la expresión especificada.

DECLARE @x AS XML;
SET @x = '<root>5</root>';

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

SELECT @y;

Este es el resultado:

<NewRoot attr="5" />

A continuación se muestra otro ejemplo en el cual se especifican expresiones para la construcción de los atributos LocationID y SetupHrs. Estas expresiones se evalúan con el XML en la columna Instruction. El valor con tipo de la expresión se asigna a los 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 es el resultado parcial:

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

Limitaciones de implementación

Éstas son las limitaciones:

  • No se admiten expresiones de atributo de varias o mixtas (cadena y expresión XQuery). Por ejemplo, tal como se muestra en la consulta siguiente, se construye XML en el que Item es una constante y el valor 5 se obtiene evaluando una expresión de consulta:

    <a attr="Item 5" />
    

    La consulta siguiente devuelve un error, ya que está mezclando una cadena constante con una expresión ({/x}) y esto no se admite:

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

    En este caso, tiene las opciones siguientes:

    • Formar el valor de atributo concatenando dos valores atómicos. Estos valores atómicos se serializan en el valor del atributo con un espacio entre los mismos:

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

      Este es el resultado:

      <a attr="Item 5" />
      
    • Use la función concat para concatenar los dos argumentos de cadena en el valor de atributo resultante:

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

      En este caso, no hay espacio agregado entre los dos valores de cadena. Si desea agregarlo, debe indicarlo explícitamente.

      Este es el resultado:

      <a attr="Item5" />
      
  • No se admiten varias expresiones como valor de atributo. Por ejemplo, la consulta siguiente genera un error:

    DECLARE @x AS XML;
    SET @x = '<x>5</x>';
    
    SELECT @x.query('<a attr="{/x}{/x}"/>');
    
  • No se admiten secuencias heterogéneas. Cualquier intento de asignar una secuencia heterogénea como un valor de atributo devuelve un error, como se muestra en el ejemplo siguiente. En este ejemplo, una secuencia heterogénea, una cadena "Item" y un elemento <x>, se especifican como el valor del atributo:

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

    Si aplica la data() función, la consulta funciona porque recupera el valor atómico de la expresión, , /xque se concatena con la cadena. A continuación se muestra una secuencia de valores atómicos:

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

    Este es el resultado:

    <a attr="Item 5" />
    
  • El orden del nodo de atributo se aplica durante la serialización, y no durante la comprobación de tipos estáticos. Por ejemplo, en la consulta siguiente se produce un error porque ésta intenta agregar un atributo después de un nodo que no es un atributo.

    SELECT CONVERT (XML, '').query('
        element x { attribute att { "pass" }, element y { "Element text" }, attribute att2 { "fail" } }
        ');
    GO
    

    La consulta anterior devuelve el siguiente error:

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

Agregar espacios de nombres

Cuando se construye XML mediante los constructores directos, es posible crear los nombres del elemento y el atributo construidos como completos mediante un prefijo de espacio de nombres. Puede enlazar el prefijo al espacio de nombres de los modos siguientes:

  • Mediante un atributo de declaración de espacio de nombres.
  • Mediante la WITH XMLNAMESPACES cláusula .
  • En el prólogo de las consultas XQuery.

Uso de un atributo de declaración de espacio de nombres para agregar espacios de nombres

En el ejemplo siguiente se usa un atributo de declaración de espacio de nombres en la construcción del elemento <a> para declarar un espacio de nombres predeterminado. La construcción del elemento <b> secundario deshace la declaración del espacio de nombres predeterminado declarado en el elemento primario.

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <a xmlns="a">
    <b xmlns=""/>
  </a>');

Este es el resultado:

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

Puede asignar un prefijo al espacio de nombres. El prefijo se especifica en la construcción del elemento <a>.

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <x:a xmlns:x="a">
    <b/>
  </x:a>');

Este es el resultado:

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

Puede anular la declaración de un espacio de nombres predeterminado en la construcción XML, pero no se puede anular la declaración de un prefijo de espacio de nombres. La consulta siguiente devuelve un error porque no se puede anular la declaración de un prefijo como se especifica en la construcción del elemento <b>.

DECLARE @x AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <x:a xmlns:x="a">
    <b xmlns:x=""/>
  </x:a>');

El espacio de nombres recientemente construido está disponible para su uso en la consulta. Por ejemplo, la consulta siguiente declara un espacio de nombres al construir el elemento , <FirstLocation>y especifica el prefijo en las expresiones para los valores de atributo LocationID y 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;

Al crear un nuevo prefijo de espacio de nombres de esta manera, se invalida cualquier declaración de espacio de nombres existente para este prefijo. Por ejemplo, la declaración de espacio de nombres , AWMI="https://someURI"en el prólogo de consulta se invalida mediante la declaración de espacio de nombres en el <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;

Uso de un prólogo para agregar espacios de nombres

En este ejemplo se ilustra el modo en el que se pueden agregar espacios de nombres al XML construido. Se declara un espacio de nombres predeterminado en el prólogo de la consulta.

DECLARE @x AS XML;
SET @x = '<x>5</x>';

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

En la construcción del elemento <b>, el atributo de declaración de espacio de nombres se especifica con una cadena vacía como su valor. Esta acción anula la declaración del espacio de nombres predeterminado que se declara en el principal.

Este es el resultado:

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

Construcción XML y control de espacios en blanco

El contenido de los elementos en la construcción de XML puede incluir caracteres de espacio en blanco. Estos caracteres se controlan de las formas siguientes:

  • Los caracteres de espacio en blanco en los URI del espacio de nombres se tratan como el tipo anyURIXSD . En concreto, este es el modo en que se controlan:

    • Los caracteres de espacio en blanco situados al principio y al final se recortan.
    • Los caracteres de espacio en blanco internos se contraen en un solo espacio.
  • Los caracteres de avance de línea situados dentro del contenido de los atributos se sustituyen por espacios. Todos los demás caracteres de espacio en blanco permanecen tal cual están.

  • Los espacios en blanco dentro de los elementos se mantienen.

En el ejemplo siguiente se ilustra el control de los espacios en blanco en la construcción de XML:

-- line feed is replaced by space.
DECLARE @x AS 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 es el resultado:

-- result
<test attr="<test attr="    my test   attr  value    "><a>

This     is  a

test

</a></test>
"><a>

This     is  a

test

</a></test>

Otros constructores XML directos

Los constructores para procesar instrucciones y comentarios XML usan la misma sintaxis que la sintaxis de construcción XML correspondiente. También se admiten constructores calculados para nodos de texto, pero se usan principalmente en XML DML para construir nodos de texto.

Nota:

Para obtener un ejemplo del uso de un constructor de nodo de texto explícito, vea el ejemplo específico de insert (XML DML).

En la consulta siguiente, el XML construido incluye un elemento, dos atributos, un comentario y una instrucción de procesamiento. Se usa una coma antes de <FirstLocation>, porque se está construyendo una secuencia.

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

Uso de constructores calculados

En este caso, se especifican las palabras clave que identifican el tipo de nodo que se desea construir. Solo se admiten las palabras clave siguientes:

  • element
  • atributo
  • text

En el caso de los nodos de elemento y atributo, estas palabras clave van seguidas del nombre del nodo y de la expresión, entre llaves, que genera el contenido del nodo. En el ejemplo siguiente, va a construir este XML:

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

Esta es la consulta que usa constructores calculados para generar el XML:

DECLARE @x AS XML;
SET @x = '';

SELECT @x.query('element root
               {
                  element ProductModel
     {
attribute PID { 5 },
text{"Some text "},
    element summary { "Some Summary" }
 }
               } ');

La expresión que genera el contenido del nodo puede especificar una expresión de consulta.

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

Los constructores de atributos y elementos calculados, tal como se definen en la especificación XQuery, permiten calcular los nombres de nodo. Cuando se usan constructores directos en SQL Server, los nombres de nodo, como el elemento y el atributo, deben especificarse como literales constantes. Por lo tanto, no hay ninguna diferencia en los constructores directos y los constructores calculados para los elementos y atributos.

En el ejemplo siguiente, el contenido de los nodos construidos se obtiene de las instrucciones de fabricación XML almacenadas en la columna Instrucciones del tipo de datos xml de la tabla 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 es el resultado parcial:

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

Otras limitaciones de implementación

Los constructores de atributos calculados no se pueden usar para declarar un nuevo espacio de nombres. Además, los siguientes constructores calculados no se admiten en SQL Server:

  • Constructores de nodos de documentos calculados
  • Constructores de instrucciones de procesamiento calculados
  • Constructores de comentarios calculados