Casos de uso generales de XQuery

En este tema se proporcionan ejemplos generales del uso de XQuery.

Ejemplos

A. Consultar descripciones del catálogo para encontrar productos y pesos

La siguiente consulta devuelve los Id. de modelo de producto y los pesos, si existen, de la descripción del catálogo de productos. La consulta genera XML con el siguiente formato:

<Product ProductModelID="…">
  <Weight>…</Weight>
</Product>

Ésta es la consulta:

SELECT CatalogDescription.query('
declare namespace p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
  <Product  ProductModelID="{ (/p1:ProductDescription/@ProductModelID)[1] }">
     { 
       /p1:ProductDescription/p1:Specifications/Weight 
     } 
  </Product>
') as Result
FROM Production.ProductModel
WHERE CatalogDescription is not null

Observe lo siguiente en la consulta anterior:

  • La palabra clave namespace del prólogo de XQuery define un prefijo de espacio de nombres que se utiliza en el cuerpo de la consulta.

  • El cuerpo de la consulta genera el XML requerido.

  • En la cláusula WHERE, se utiliza el método exist() para buscar únicamente las filas que contienen descripciones del catálogo de productos; es decir, el XML que contiene el elemento <ProductDescription>.

El resultado es el siguiente:

<Product ProductModelID="19"/>
<Product ProductModelID="23"/> 
<Product ProductModelID="25"/> 
<Product ProductModelID="28"><Weight>Varies with size.</Weight></Product>
<Product ProductModelID="34"/>
<Product ProductModelID="35"/>

La siguiente consulta recupera la misma información, pero sólo de los modelos de productos en cuya descripción de catálogo se incluya el peso (el elemento <Weight>) en las especificaciones (el elemento <Specifications>). En este ejemplo se utiliza WITH XMLNAMESPACES para declarar el prefijo pd y su enlace de espacio de nombres. De este modo, el enlace no se describe en el método query() ni en el método exist().

WITH XMLNAMESPACES ('https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription' AS pd)
SELECT CatalogDescription.query('
          <Product  ProductModelID="{ (/pd:ProductDescription/@ProductModelID)[1] }">
                 { 
                      /pd:ProductDescription/pd:Specifications/Weight 
                 } 
          </Product>
') as x
FROM Production.ProductModel
WHERE CatalogDescription.exist('/pd:ProductDescription/pd:Specifications//Weight ') = 1

En la consulta anterior, el método exist() del tipo de datos xml de la cláusula WHERE comprueba si hay un elemento <Weight> en el elemento <Specifications>.

B. Encontrar Id. de modelos de productos para modelos de productos en cuyas descripciones de catálogo se incluyan imágenes de pequeño tamaño y ángulo frontal

La descripción del catálogo de productos XML incluye imágenes de los productos (el elemento <Picture>). Cada imagen tiene varias propiedades. Entre estas propiedades se incluyen el ángulo de la imagen (el elemento <Angle>) y el tamaño (el elemento <Size>).

Para los modelos de productos en cuyas descripciones de catálogo se incluyen imágenes de pequeño tamaño y ángulo frontal, la consulta genera XML con el siguiente formato:

< Product ProductModelID="…">
  <Picture>
    <Angle>front</Angle>
    <Size>small</Size>
  </Picture>
</Product>
WITH XMLNAMESPACES ('https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription' AS pd)
SELECT CatalogDescription.query('
   <pd:Product  ProductModelID="{ (/pd:ProductDescription/@ProductModelID)[1] }">
      <Picture>
         {  /pd:ProductDescription/pd:Picture/pd:Angle } 
         {  /pd:ProductDescription/pd:Picture/pd:Size } 
      </Picture>
   </pd:Product>
') as Result
FROM  Production.ProductModel
WHERE CatalogDescription.exist('/pd:ProductDescription/pd:Picture') = 1
AND   CatalogDescription.value('(/pd:ProductDescription/pd:Picture/pd:Angle)[1]', 'varchar(20)')  = 'front'
AND   CatalogDescription.value('(/pd:ProductDescription/pd:Picture/pd:Size)[1]', 'varchar(20)')  = 'small'

Observe lo siguiente en la consulta anterior:

  • En la cláusula WHERE, se utiliza el método exist() para recuperar únicamente las filas que disponen de descripciones del catálogo de productos con el elemento <Picture>.

  • La cláusula WHERE utiliza el método value() dos veces para comparar los valores de los elementos <Size> y <Angle>.

Éste es un resultado parcial:

<p1:Product 
  xmlns:p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription" 
  ProductModelID="19">
  <Picture>
    <p1:Angle>front</p1:Angle>
    <p1:Size>small</p1:Size>
  </Picture>
</p1:Product>
...

C. Crear una lista plana del nombre de modelo de producto y pares de características, con cada par incluido dentro del elemento <Características>

En la descripción de catálogo del modelo de producto, el XML incluye varias características del producto. Todas estas características se incluyen en el elemento <Features>. La consulta utiliza la construcción de XML (XQuery) para generar el XML requerido. La expresión incluida entre llaves se reemplaza por el resultado.

SELECT CatalogDescription.query('
declare namespace p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
  for $pd in /p1:ProductDescription,
   $f in $pd/p1:Features/*
  return
   <Feature>
     <ProductModelName> { data($pd/@ProductModelName) } </ProductModelName>
     { $f }
  </Feature>        
') as x
FROM Production.ProductModel
WHERE ProductModelID=19

Observe lo siguiente en la consulta anterior:

  • $pd/p1:Features/* sólo devuelve los nodos secundarios del nodo de elemento de <Features>, pero $pd/p1:Features/node() devuelve todos los nodos, donde se incluyen los nodos de elemento, nodos de texto, instrucciones de procesamiento y comentarios.

  • Los dos bucles FOR generan un producto cartesiano a partir del que se devuelven el nombre de producto y la característica individual.

  • ProductName es un atributo. La construcción XML de esta consulta lo devuelve como un elemento.

Éste es un resultado parcial:

<Feature>
 <ProductModelName>Mountain 100</ProductModelName>
 <ProductModelID>19</ProductModelID>
 <p1:Warranty 
   xmlns:p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain">
    <p1:WarrantyPeriod>3 year</p1:WarrantyPeriod>
    <p1:Description>parts and labor</p1:Description>
 </p1:Warranty>
</Feature>
<Feature>
 <ProductModelName>Mountain 100</ProductModelName>
 <ProductModelID>19</ProductModelID>
 <p2:Maintenance xmlns:p2="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain">
    <p2:NoOfYears>10</p2:NoOfYears>
    <p2:Description>maintenance contact available through your dealer 
           or any AdventureWorks retail store.</p2:Description>
    </p2:Maintenance>
</Feature>
...
...    

D. A partir de la descripción de catálogo de un modelo de producto, mostrar el nombre de modelo de producto, el Id. de modelo y las características, todo ello agrupado dentro de un elemento <Product>

A partir de la información almacenada en la descripción de catálogo del modelo de producto, la siguiente consulta muestra el nombre de modelo de producto, el Id. de modelo y las características, todo ello agrupado dentro de un elemento <Product>.

SELECT ProductModelID, CatalogDescription.query('
     declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
     <Product>
         <ProductModelName> 
           { data(/pd:ProductDescription/@ProductModelName) } 
         </ProductModelName>
         <ProductModelID> 
           { data(/pd:ProductDescription/@ProductModelID) } 
         </ProductModelID>
         { /pd:ProductDescription/pd:Features/* }
     </Product>        
') as x
FROM Production.ProductModel
WHERE ProductModelID=19

Éste es un resultado parcial:

<Product>
  <ProductModelName>Mountain 100</ProductModelName>
  <ProductModelID>19</ProductModelID>
  <p1:Warranty>... </p1:Warranty>
  <p2:Maintenance>...  </p2:Maintenance>
  <p3:wheel xmlns:p3="https://www.adventure-works.com/schemas/OtherFeatures">High performance wheels.</p3:wheel>
  <p4:saddle xmlns:p4="https://www.adventure-works.com/schemas/OtherFeatures">
    <p5:i xmlns:p5="http://www.w3.org/1999/xhtml">Anatomic design</p5:i> and made from durable leather for a full-day of riding in comfort.</p4:saddle>
  <p6:pedal xmlns:p6="https://www.adventure-works.com/schemas/OtherFeatures">
    <p7:b xmlns:p7="http://www.w3.org/1999/xhtml">Top-of-the-line</p7:b> clipless pedals with adjustable tension.</p6:pedal>
   ...

E. Recuperar descripciones de características de modelos de productos

La siguiente consulta genera XML que incluye un elemento <Product> con los atributos ProducModelID y ProductModelName, y las dos primeras características del producto. Las dos primeras características del producto son, concretamente, los dos primeros elementos secundarios del elemento <Features>. Si hay más características, devuelve un elemento <There-is-more/> vacío.

SELECT CatalogDescription.query('
declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
     <Product> 
          { /pd:ProductDescription/@ProductModelID }
          { /pd:ProductDescription/@ProductModelName } 
          {
            for $f in /pd:ProductDescription/pd:Features/*[position()<=2]
            return
            $f 
          }
          {
            if (count(/pd:ProductDescription/pd:Features/*) > 2)
            then <there-is-more/>
            else ()
          } 
     </Product>        
') as Result
FROM Production.ProductModel
WHERE CatalogDescription is not NULL

Observe lo siguiente en la consulta anterior:

  • La estructura de bucle FOR ... RETURN recupera las dos primeras características del producto. La función position() se utiliza para buscar la posición de los elementos en la secuencia.

F. Buscar nombres de elementos que terminen en "ons" en la descripción del catálogo de productos

La siguiente consulta realiza búsquedas en las descripciones del catálogo y devuelve todos los elemento del elemento <ProductDescription> cuyo nombre termine en "ons".

SELECT ProductModelID, CatalogDescription.query('
     declare namespace p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
      for $pd in /p1:ProductDescription/*[substring(local-name(.),string-length(local-name(.))-2,3)="ons"]
      return 
          <Root>
             { $pd }
          </Root>
') as Result
FROM Production.ProductModel
WHERE CatalogDescription is not NULL

Éste es un resultado parcial:

ProductModelID   Result
-----------------------------------------
         19        <Root>       
                     <p1:Specifications xmlns:p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription">        
                          ...       
                     </p1:Specifications>       
                   </Root>        

G. Buscar descripciones resumidas que contengan la palabra "Aerodynamic"

La siguiente consulta recupera los modelos de productos cuyas descripciones de catálogo incluyen la palabra "Aerodynamic" en la descripción resumida:

WITH XMLNAMESPACES ('https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription' AS pd)
SELECT ProductModelID, CatalogDescription.query('
          <Prod >
             { /pd:ProductDescription/@ProductModelID }
             { /pd:ProductDescription/pd:Summary }
          </Prod>
 ') as Result
FROM Production.ProductModel
WHERE CatalogDescription.value('
     contains( string( (/pd:ProductDescription/pd:Summary)[1] ),"Aerodynamic")','bit') = 1

Tenga en cuenta que la consulta SELECT especifica los métodos query() y value() del tipo de datos xml. Por lo tanto, en lugar de repetir la declaración de espacios de nombres dos veces en dos prólogos de consulta distintos, se utiliza el prefijo pd en la consulta y se define una sola vez mediante WITH XMLNAMESPACES.

Observe lo siguiente en la consulta anterior:

  • La cláusula WHERE se utiliza para recuperar únicamente las filas en las que la descripción de catálogo contiene la palabra "Aerodynamic" en el elemento <Summary>.

  • Para ver si la palabra está incluida en el texto se utiliza la función contains().

  • El método value() del tipo de datos xml compara el valor devuelto por la función contains() con 1.

El resultado es el siguiente:

ProductModelID Result      
-------------- ------------------------------------------
28     <Prod ProductModelID="28">
        <pd:Summary xmlns:pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription">
       <p1:p xmlns:p1="http://www.w3.org/1999/xhtml">
         A TRUE multi-sport bike that offers streamlined riding and a
         revolutionary design. Aerodynamic design lets you ride with the 
         pros, and the gearing will conquer hilly roads.</p1:p>
       </pd:Summary>
      </Prod>  

H. Buscar modelos de productos en cuyas descripciones de catálogo no se incluyan imágenes del modelo de producto

La siguiente consulta recupera Id. de modelos de productos (ProductModelID) para todos los modelos de productos en cuyas descripciones de catálogo no se incluya ningún elemento <Picture>.

SELECT  ProductModelID
FROM    Production.ProductModel
WHERE   CatalogDescription is not NULL
AND     CatalogDescription.exist('declare namespace p1="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
     /p1:ProductDescription/p1:Picture
') = 0

Observe lo siguiente en la consulta anterior:

  • Si el método exist() de la cláusula WHERE devuelve False (0), se devuelve el Id. de modelo de producto. De lo contrario, no se devuelve.

  • Como todas las descripciones de productos incluyen un elemento <Picture>, el conjunto de resultados está vacío en este caso.