Comience a usar pureXML en DB2 V9 ya mismo, Parte 4: Consulta de datos XML en DB2 con XQuery

IBM DB2®V9 para Linux®, UNIX® y Windows® ahora ofrece un importante soporte de almacenamiento, gestión y búsqueda de datos XML, denominado pureXML. Esta serie lo ayuda a dominar rápidamente las nuevas características de XML a través de varios artículos paso a paso que le explican cómo realizar tareas fundamentales. En este artículo, aprenda a consultar datos almacenados en columnas XML usando XQuery. [Nota del Editor, 25 de marzo de 2010: La versión original de este artículo fue escrita en 2006 y ha sido actualizada para incluir los cambios de las versiones 9.5 y 9.7 de DB2.]

Cynthia M. Saracco, Senior Software Engineer, EMC

Cindy Saracco photoC. M. Saracco trabaja en el Silicon Valley Laboratory de IBM, en la organización DB2 XML. Se ocupa de la gestión de bases de datos, XML, el desarrollo de aplicaciones Web y otros temas relacionados.


Nivel de autor profesional en developerWorks

Don Chamberlin, IBM Fellow, EMC

Don Chamberlin, IBM Fellow de Almaden Research Center, es uno de los representantes de IBM pertenecientes al XML Query Working Group de W3C. También es coautor de la propuesta de lenguaje Quilt, la cual sentó las bases del diseño de XQuery. Don es famoso por ser coinventor del lenguaje de base de datos SQL y autor de dos libros sobre el sistema de base de datos DB2. Además de tener una licenciatura del Harvey Mudd College y un doctorado de la Universidad de Stanford, es ACM Fellow y miembro de la Academia Nacional de Ingeniería de Estados Unidos.



05-05-2010 (Primera publicación 05-05-2010)

Probablemente esté al tanto de la sensación que está causando DB2 V9, el primer sistema de gestión de base de datos de IBM que soporta tanto estructuras tabuladas (basadas en SQL) como jerárquicas (basadas en XML). Los anteriores artículos de esta serie resumieron las nuevas características XML de DB2, describieron cómo crear objetos de bases de datos y poblarlos con datos XML y explicaron cómo trabajar con datos XML usando SQL y SQL/XML. Este artículo continúa describiendo las capacidades XML de DB2 y se centra en el nuevo soporte de XQuery.

DB2 trata a XQuery como lenguaje de primera clase, lo cual permite que los usuarios escriban expresiones XQuery directamente, en lugar de tener que usar instrucciones SQL para incrustar o contener consultas XQuery. Además, el motor de consultas de DB2 procesa consultas XQuery nativamente, es decir que analiza, evalúa y optimiza consultas XQuery sin traducirlas a SQL de manera no visible en ningún momento. Si usted prefiere escribir consultasbilinguesque incluyan expresiones XQuery y SQL, desde ya que DB2 procesará y optimizará también este tipo de consultas.

Así como se hizo con SQL/XML en la Parte 3 de esta serie, este artículo desarrolla una serie de tareas de consultas comunes y le muestra cómo usar XQuery para cumplir con sus objetivos. Primero, analicemos brevemente las diferencias entre XQuery y SQL.

Acerca de XQuery

XQuery se diferencia de SQL en varios aspectos claves, principalmente porque estos lenguajes fueron diseñados para trabajar con modelos de datos distintos que poseen características diferentes. Los documentos XML contienen jerarquías y poseen un orden inherente. Las estructuras de datos tabulares soportadas por sistemas de gestión de bases de datos (DBMS) basados en SQL son planas y basadas en conjuntos; por lo tanto, las filas aparecen desordenadas.

Estos modelos de datos distintos generan una serie de diferencias fundamentales en sus respectivos lenguajes de consulta, por ejemplo:

  • XQuery soporta expresiones de ruta mediante los cuales los programadores navegan dentro de la estructura jerárquica de XML, mientras que SQL plano (sin extensiones XML) no soporta estas expresiones.
  • XQuery soporta tanto datos tipificados como no tipificados, mientras que los datos SQL siempre se definen con un tipo específico.
  • XQuery no posee valores nulos dado que los documentos XML omiten los datos faltantes o desconocidos. Como sabemos, SQL usa valores nulos para representar valores de datos faltantes o desconocidos.
  • XQuery devuelve secuencias de datos XML, mientras que SQL devuelve conjuntos de resultados de varios tipos de datos SQL diferentes.

El anterior es solamente un resumen de las diferencias fundamentales entre XQuery y SQL. Su enumeración exhaustiva se encuentra fuera del alcance de este artículo introductorio. Si desea leer una lista más detallada de las diferencias, consulte el artículo del IBM Systems Journal. El presente se concentra en ciertos aspectos básicos del lenguaje XQuery y en cómo usarlos para consultar datos XML en DB2 V9.


Base datos de muestra

Las consultas proporcionadas en este artículo acceden a las tablas de muestra creadas en la Parte 1 de la serie. A modo de repaso, el Listado 1 define las tablas de muestra items(artículos) y clients(clientes).

Listado 1. Definiciones de tablas
create table items ( id int primary key not null, brandname
varchar(30), itemname varchar(30), sku int, srp decimal(7,2), comments xml ) create
table clients( id int primary key not null, name varchar(50), status varchar(10),
                contactinfo xml )

El Listado 2 incluye los datos XML de muestra de la columna items.comments, mientras que el Listado 3 incluye los datos XML de muestra de la columna clients.contactinfo. Los ejemplos de consultas proporcionados de aquí en adelante referenciarán a elementos específicos incluidos en uno de estos documentos XML o en ambos.

Listado 2. Documento XML de muestra almacenado en la columna Comments de la tabla Items
<Comments> <Comment>
                <CommentID>133</CommentID>
                <ProductID>3926</ProductID>
                <CustomerID>8877</CustomerID>
                <Message>Heels on shoes wear out too quickly.</Message>
                <ResponseRequested>No</ResponseRequested>
                </Comment> <Comment>
                <CommentID>514</CommentID>
                <ProductID>3926</ProductID>
                <CustomerID>3227</CustomerID>
                <Message>Where can I find a supplier in San
                Jose?</Message>
                <ResponseRequested>Yes</ResponseRequested>
                </Comment> </Comments>
Listado 3. Documento XML de muestra almacenado en la columna Contactinfo de la tabla Clients
<Client> <Address>
                <street>5401 Julio Ave.</street> <city>San
                Jose</city> <state>CA</state>
                <zip>95116</zip> </Address>
                <phone> <work>4084630000</work>
                <home>4081111111</home>
                <cell>4082222222</cell> </phone>
                <fax>4087776666</fax>
                <email>love2shop@yahoo.com</email>
                </Client>

Entorno de consultas

Todas las consultas proporcionadas en este artículo están diseñadas para emitirse interactivamente a través del procesador de la línea de comandos de DB2 o de DB2 Command Editor del DB2 Control Center. Las imágenes de pantallas e instrucciones de este artículo se basan en el último. (También IBM Data Studio e IBM Optim Development Studio traen incluido un Developer Workbench basado en Eclipse que ayuda a los programadores a construir consultas gráficamente. Este artículo no aborda cuestiones de desarrollo de aplicaciones o del uso de Development Studio).

Para usar DB2 Command Editor, inicie Control Center y seleccione Tools > Command Editor(Herramientas > Editor de comandos). Aparecerá una ventana similar a la mostrada en la Figura 1.

Figura 1. DB2 Command Editor (puede iniciarse desde DB2 Control Center)
DB2 Command Editor (puede iniciarse desde DB2 Control Center)

Ingrese sus consultas en el recuadro superior y haga clic en la flecha verde ubicada en la esquina superior izquierda para ejecutarlas. Observe los datos de salida mostrados en el recuadro inferior o en otra pestaña denominada Query Results (Resultados de consultas).


Ejemplos de XQuery

Al igual que la Parte 3 de esta serie, este artículo toca varios escenarios de negocios comunes para mostrarle cómo usar XQuery para cumplir con solicitudes de datos XML. Además, se analizan situaciones de mayor complejidad en las cuales se requiere incrustar SQL dentro de XQuery.

XQuery proporciona varios tipos de expresiones distintos que pueden combinarse de la forma deseada. Cada expresión devuelve una lista de valores que pueden usarse como datos de entrada de otras expresiones. El resultado de nivel superior será el resultado de la consulta.

Este artículo se centra en dos tipos importantes de expresiones XQuery: expresiones FLWOR y expresiones de ruta. Una expresión FLWOR es muy similar a una expresión SELECT-FROM-WHERE de SQL: se usa para iterar en una lista de elementos y opcionalmente devolver cierto componente que se computa de cada elemento. En contraposición, una expresión de ruta navega en una jerarquía de elementos XML y devuelve los elementos que se encuentran al final de la ruta.

Al igual que las expresiones SELECT-FROM-WHERE de SQL, las expresiones FLWOR de XQuery pueden contener varias cláusulas que comienzan con ciertas palabras claves. Las siguientes palabras claves se usan para iniciar cláusulas de expresiones FLWOR:

  • for: Itera a través de una secuencia de entrada, enlazando a su vez una variable a cada elemento de entrada.
  • let: Declara una variable y le asigna un valor, el cual puede ser una lista que contenga elementos múltiples
  • where: Especifica criterios de filtrado de resultados de consultas.
  • order by: Especifica el criterio de ordenamiento del resultado.
  • return: Define el resultado que será devuelto.

Una expresión de ruta de XQuery está compuesta por una serie de pasos separados por caracteres de barra. En la forma simple, cada paso navega de forma descendente dentro de la jerarquía XML buscando el elemento secundario correspondiente al elemento devuelto por el paso anterior. Cada paso de la expresión de ruta puede además contener un predicado que filtre los elementos devueltos por el paso y retenga solamente los elementos que cumplan con cierta condición. Por ejemplo, para una variable $clients que se encuentra enlazada con una lista de documentos XML que contienen elementos <Client>, la expresión de ruta de cuatro pasos $clients/Client/Address[state = "CA"]/zip devolverá la lista de códigos postales de los clientes con direcciones de California.

En muchos casos, es posible escribir una consulta usando una expresión FLWOR o una expresión de ruta.

Uso de DB2 XQuery como lenguaje de consulta de nivel superior

Para ejecutar una consulta XQuery directamente en DB2 V9 (en lugar de incrustarla en una instrucción SQL), debemos iniciar la consulta con la palabra clave xquery. Esto le indica a DB2 que debe invocar al analizador XQuery para procesar la solicitud. Tome en cuenta que esto solamente será necesario si está usando XQuery como lenguaje de nivel superior. Si, por el contrario, usted ha incrustado expresiones XQuery en SQL, no necesitará iniciarlas con la palabra clave xquery. Como este artículo usa XQuery como lenguaje primario, todas las consultas comienzan con xquery.

Al ejecutar XQuery como lenguaje primario, es necesario contar con una fuente de datos de entrada. Una forma en la que XQuery puede obtener datos de entrada es llamando a una función denominada db2-fn:xmlcolumn con un parámetro que identifique el nombre de tabla y el nombre de columna de una columna XML dentro de una tabla DB2. La función db2-fn:xmlcolumn devuelve la secuencia de documentos XML que se encuentra almacenada en la columna proporcionada. Por ejemplo, la consulta mostrada en el Listado 4 devuelve una secuencia de documentos XML que contiene información de contacto de clientes.

Listado 4. XQuery simple que devuelve datos de contacto de clientes
xquery db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')

Como vimos en el esquema de base de datos (ver la sección "Base de datos de muestra"), los documentos XML están almacenados en la columna Contactinfo de la tabla Clients. Observe que los nombres de columna y tabla en este caso están especificados en mayúsculas. Esto se debe a que los nombres de tablas y columnas se pasan a mayúsculas antes de escribirse en el catálogo interno de DB2. De lo contrario, como XQuery distingue mayúsculas de minúsculas, los nombres de tablas y columnas en minúsculas no coincidirían con los nombres en mayúsculas en el catálogo DB2.

Recuperación de elementos XML específicos

Ahora abordemos una tarea básica. Supongamos que deseamos recuperar todos los números de fax de clientes que hayan suministrado esta información. El Listado 5 muestra una forma de escribir una consulta que cumpla con esta función.

Listado 5. Expresión FLWOR para recuperar datos de números de fax de clientes
xquery for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
                return $y

La primera línea le indica a DB2 que debe invocar al analizador XQuery. La siguiente línea le indica a DB2 que debe iterar en los subelementos fax del elemento Client contenido en la columna CLIENTS.CONTACTINFO. Cada elemento fax se enlaza a la variable $y. La tercera línea indica que, con cada iteración, se devolverá el valor de $y. El resultado es una secuencia de elementos XML, como muestra el Listado 6.

Listado 6. Datos de salida de muestra de la consulta anterior
<fax>4081112222</fax>
                <fax>5559998888</fax>

Al margen, los datos de salida también contendrán información que no vienen al caso en este artículo: datos de la versión XML y de codificación, por ejemplo: <?xml version="1.0" encoding="windows-1252" ?> e información de espacio de nombres XML como <fax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">. Para mejorar la visualización de los datos de salida, este tipo de información se ha omitido en el artículo. Sin embargo, estos datos pueden resultar útiles para una serie de aplicaciones XML. Si usted usa el procesador de línea de comandos de DB2 para ejecutar sus consultas, puede usar la opción -d para suprimir la información de declaración XML y la opción -i para imprimir los resultados de forma atractiva a la vista.

La consulta mostrada en el Listado 5 podría expresarse de forma más concisa como expresión de ruta de tres pasos, como muestra el Listado 7.

Listado 7. Expresiones de ruta para recuperar datos de número de fax de clientes
xquery db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax

El primer paso de la expresión de ruta llama a la función db2-fn:xmlcolumn para obtener una lista de documentos XML de la columna CONTACTINFO de la tabla CLIENTS. El segundo paso devuelve todos los elementos de Client contenidos en estos documentos y el tercer paso devuelve los elementos de fax incrustados dentro de dichos elementos de Client.

Si usted no desea obtener fragmentos XML con su consulta y prefiere obtener una representación de texto de los valores de elementos XML que califiquen, puede invocar a la función text() dentro de la cláusula return, como muestra el Listado 8.

Listado 8. Dos consultas para recuperar una representación de texto de los datos de números de fax de clientes
xquery for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
                return $y/text() (or) xquery
                db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax/text()

Los datos de salida de estas consultas serán similares a los mostrados en el Listado 9.

Listado 9. Datos de salida de muestra de las solicitudes anteriores
4081112222 5559998888

Los resultados de las consultas de muestra son relativamente simples debido a que el elemento fax está basado en un tipo de datos primitivo. Los elementos, por supuesto, podrían estar basados en tipos complejos, es decir, que podrían contener subelementos (o jerarquías incrustadas). El elemento Address (Dirección) de la información de contacto de clientes es un ejemplo de estos casos. En base al esquema definido en la Parte 2 de esta serie, este elemento contendría los siguientes datos: calle, número de domicilio, ciudad, estado y código postal. Imagine los datos que podría devolver la consulta XQuery del Listado 10.

Listado 10. Expresión FLWOR para recuperar un tipo XML complejo
xquery for $y in
                db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address return $y

Si usted imaginó una secuencia de fragmentos XML que contenía los elementos de Address y sus correspondientes subelementos, estaba en lo cierto. El Listado 11 muestra un ejemplo:

Listado 11. Datos de salida de muestra de la consulta anterior
<Address> <street>5401 Julio
                Ave.</street> <city>San Jose</city>
                <state>CA</state>
                <zip>95116</zip> </Address> . . .
                <Address> <street>1204 Meridian
                Ave.</street> <apt>4A</apt>
                <city>San Jose</city>
                <state>CA</state>
                <zip>95124</zip> </Address>

Nota: El formato de estos datos de salida de muestra se ha simplificado para una mejor visualización. DB2 Command Editor muestra el registro de dirección de cada cliente en una línea diferente.

Filtrado de valores de elementos XML

Los ejemplos anteriores de XQuery pueden refinarse selectivamente. Por ejemplo, piense cómo podríamos devolver las direcciones de correspondencia de clientes con el código postal 95116 de Estados Unidos.

Como ya se habrá imaginado, la cláusula where de XQuery permite filtrar los resultados en base al valor del elemento zip (código postal) de los documentos XML. El Listado 12 muestra cómo agregar una cláusula where a la expresión FLWOR del Listado 10 para obtener únicamente las direcciones deseadas.

Listado 12. Expresión FLWOR con una nueva cláusula "where"
xquery for $y in
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address where $y/zip="95116" return
                $y

La cláusula where agregada es fácil de comprender. La cláusula for enlaza la variable $y a cada dirección, una por vez. La cláusula where contiene una expresión de ruta pequeña que navega desde cada dirección al elemento zip incrustado. La cláusula where será verdadera (se retiene la dirección) únicamente si el valor de dicho elemento zip es igual a 95116.

Se podría obtener el mismo resultado agregando un predicado a la expresión de ruta, como muestra el Listado 13.

Listado 13. Expresión de ruta con predicado de filtrado adicional
xquery
                db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address[zip="95116"]

Por supuesto, es posible filtrar los valores de código postal y devolver elementos no relacionados con direcciones de calles. Asimismo, también se podrán filtrar valores de elementos XML múltiples en una misma consulta. La consulta del Listado 14 devuelve información de e-mail de clientes que viven en áreas con un código postal específico de Nueva York (10011) o en cualquier parte de la ciudad de San José.

Listado 14. Filtrado de valores de elementos XML múltiples con una expresión FLWOR
xquery for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
 where $y/Address/zip="10011" or
 $y/Address/city="San Jose" 
 return $y/email

Observe que la cláusula for se modificó para enlazar la variable $y a elementos de Client en lugar de a elementos de Address. Esto permite filtrar los elementos de Client mediante un parte del subárbol (Address) y devolver otra parte del subárbol (email). Las expresiones de ruta de la cláusula where y la cláusula return deben escribirse en relación con el elemento que se enlaza con la variable (en este caso, $y).

La misma consulta puede expresarse de forma más concisa como expresión de ruta, como muestra el Listado 15.

Listado 15. Filtrado de valores de elementos XML múltiples con una expresión de ruta
xquery
                db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client[Address/zip="10011" or
                Address/city="San Jose"]/email;

Lo que no resulta tan obvio al revisar cualquiera de las dos formas de consulta es que los resultados devueltos diferirán de lo que podría esperar un programador SQL. Se observarán dos diferencias considerables:

  1. No obtendremos datos XML de clientes que califican pero que no proporcionaron su dirección de e-mail. En otras palabras, si tuviéramos 1000 clientes que viven en San José o dentro del área del código postal 10011 y solamente 700 clientes hubieran proporcionado una única dirección de e-mail cada uno, obtendremos una lista de 700 e-mails. Esto se debe a una diferencia fundamental entre XQuery y SQL que mencionamos anteriormente: XQuery no usa valores nulos.
  2. No sabremos cuáles direcciones de e-mail derivaron del mismo documento XML. En otras palabras, si tuviéramos 700 clientes que viven en San José o dentro del área del código postal 10011 y cada uno hubiera proporcionado dos direcciones de e-mail, obtendremos una lista de 1400 elementos de e-mail.Nose obtendrá una secuencia de 700 registros compuestos por dos direcciones de e-mail.

Ambas situaciones podrían ser adecuadas en algunos casos, pero podrían resultar inadecuadas en otros. Por ejemplo, si necesitásemos enviar un aviso por e-mail a todas las cuentas registradas que califiquen, la iteración en la lista de direcciones de e-mail de clientes en formato XML sería una opción fácil de aplicar. Sin embargo, si quisiéramos enviar un único aviso a todos los clientes, incluso a aquellos que sólo proporcionaron su dirección de calle, la consulta XQuery antes mostrada no resultará suficiente.

Existen varias formas de reescribir esta consulta para lograr que los resultados devueltos representen de alguna manera la información faltante e indiquen cuándo se derivaron direcciones de e-mail múltiples del mismo registro de cliente, es decir, del mismo documento XML (ampliaremos este tema más adelante). Sin embargo, si únicamente quisiéramos recuperar una lista que contenga un e-mail por cliente que califique, sólo tendríamos que realizar un pequeño cambio en la cláusula return de la consulta anterior.

Listado 16. Recuperación de únicamente el primer elemento de email por cliente
xquery for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
                where $y/Address/zip="10011" or $y/Address/city="San Jose"
                return $y/email[1]

Esta consulta logra que DB2 devuelva el primer elemento de e-mail que encuentre dentro de cada documento XML que califique (registro de contacto de cliente). Si la consulta no encuentra una dirección de e-mail de un cliente que califica, no devolverá ningún dato de dicho cliente.

Transformación de datos XML de salida

Un importante aspecto de XQuery es su capacidad de transformar los datos XML de salida de una forma de XML en otra. Por ejemplo, podemos usar XQuery para recuperar la totalidad o una parte de los documentos XML almacenados y convertir los datos de salida a HTML para su mejor visualización en un navegador Web. La consulta del Listado 17 recupera las direcciones de los clientes, ordena los resultados por código postal y convierte los datos de salida a elementos XML que forman parte de una lista HTML no ordenada.

Listado 17. Consulta de datos XML en DB2 y devolución de resultados en formato HTML
xquery <ul> { for $y in
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address order by $y/zip return
                <li>{$y}</li> } </ul>

La consulta se desglosa de la siguiente manera:

  • La consulta comienza de forma clara, con la palabra clave xquery, lo cual indica al analizador de DB2 que se usará XQuery como lenguaje primario.
  • La segunda línea genera un marcado HTML para incluir una lista no ordenada (<ul>) en los resultados. También abre el primer par de llaves usado en esta consulta. Las llaves indican a DB2 que la expresión entre llaves debe ser evaluada y procesada en lugar de ser tratada como una cadena literal.
  • La tercera línea itera en las direcciones de clientes, enlazando la variable $y a cada elemento de dirección por vez.
  • La cuarta línea incluye una nueva cláusula order by, la cual especifica que los resultados deberán devolverse en orden ascendente (orden predeterminado) en base al criterio customer zip codes (códigos postales de clientes); subelemento de zip de cada dirección enlazada a $y.
  • La cláusula return indica que los elementos de Address deberán rodearse con etiquetas de elementos de lista HTML antes de su devolución.
  • La última línea finaliza la consulta y completa la etiqueta de lista no ordenada HTML.

Los datos de salida serán similares a los mostrados en el Listado 18.

Listado 18. Datos HTML de salida de muestra de la consulta anterior
<ul> <li> <Address>
                <street>9407 Los Gatos Blvd.</street>
                <city>Los Gatos</city>
                <state>CA</state>
                <zip>95032</zip> </Address>
                </li> <li> <Address>
                <street>4209 El Camino Real</street>
                <city>Mountain View</city>
                <state>CA</state>
                <zip>95033</zip> </Address>
                </li> . . . </ul>

Ahora desarrollemos un tema que surgió antes: cómo escribir una consulta XQuery que indique valores faltantes en los resultados devueltos y además indique cuando un único documento XML (por ejemplo, único registro de cliente) contenga elementos que se repiten (como direcciones de e-mail múltiples). Una solución posible es contener los datos de salida devueltos en un elemento XML nuevo, como muestra la consulta del Listado 19:

Listado 19. Indicación de valores faltantes y elementos que se repiten en un resultado de consulta XQuery
xquery for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
                where $y/Address[zip="10011"] or $y/Address[city="San Jose"] return
                <emailList> {$y/email} </emailList>

La ejecución de esta consulta devolverá una secuencia de elementos emailList con un elemento por cada registro de cliente que califique. Cada elemento emailList contiene datos de e-mail. Si DB2 encuentra una única dirección de e-mail en un registro de cliente, devolverá dicho elemento y su valor. Si encuentra direcciones de e-mail múltiples, devolverá todos los elementos de e-mail y sus valores. Por último, si no encuentra ninguna dirección de e-mail, devolverá un elemento emailList vacío. Por consiguiente, los datos de salida serán similares a los mostrados en el Listado 20.

Listado 20. Datos de salida de muestra de la consulta anterior
<emailList>
                <email>love2shop@yahoo.com</email>
                </emailList> <emailList/> <emailList>
                <email>beatlesfan36@hotmail.com</email>
                <email>lennonfan36@hotmail.com</email>
                </emailList> . . .

Uso de lógica condicional

La habilidad de XQuery de transformar datos de salida de XML puede combinarse con el soporte incorporado de lógica condicional para reducir la complejidad del código de aplicación. Tomemos un ejemplo simple. La tabla Items incluye una columna XML que contiene comentarios de los clientes sobre los productos. Para los casos de clientes que hayan solicitado una respuesta a sus comentarios, podríamos crear nuevos elementos Action que contengan el ID de producto, el ID de cliente y el mensaje para rutear esta información a la persona encargada de su gestión. Sin embargo, los comentarios que no requieren una respuesta siguen siendo importantes para la empresa y no deben ignorarse. Entonces, crearemos un elemento Info que sólo contenga el ID de producto y el mensaje. A continuación se muestra una expresión if-then-else de XQuery que efectúa las tareas descriptas:

Listado 21. Uso de expresión "if-then-else" en una consulta XQuery
xquery for $y in
                db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment return ( if
                ($y/ResponseRequested = 'Yes') then <action> {$y/ProductID,
                $y/CustomerID, $y/Message} </action> else ( <info>
                {$y/ProductID, $y/Message} </info> ) )

La mayoría de los aspectos de esta consulta ya nos resultan familiares, por lo cual nos centraremos en la lógica condicional. La cláusula if determina si el valor del subelemento ResponseRequested de un determinado comentario es igual a Yes (Sí). De serlo, se evalúa la cláusula then, lo cual hace que DB2 devuelva un nuevo elemento (action) que contiene tres subelementos: ProductID, CustomerID y Message. De lo contrario, se evaluará la cláusula else y DB2 devolverá un elemento Info que contenga únicamente el ID de producto y los datos del mensaje.

Uso de la cláusula let

Ya vimos cómo usar todas las partes de una expresión FLWOR excepto la cláusula let. Esta cláusula se usa para asignar un valor (que probablemente contendrá una lista de varios elementos) a una variable que puede usarse en otras cláusulas de la expresión FLWOR.

Supongamos que queremos hacer una lista de la cantidad de comentarios que recibió cada producto. Esto es posible con la consulta del Listado 22.

Listado 22. Uso de la cláusula "let"
xquery for $p in distinct-values
                (db2-fn:xmlcolumn('ITEMS.COMMENTS')
                /Comments/Comment/ProductID) let $pc :=
                db2-fn:xmlcolumn('ITEMS.COMMENTS') 
                /Comments/Comment[ProductID = $p] return
                <product> <id> { $p } </id>
                <comments> { count($pc) } </comments>
                </product>

La función distinct-valuesde la cláusula for devuelve una lista de todos los valores diferentes de ProductID que se encuentran dentro de los comentarios de la columna Comments de la tabla Items. La cláusula for enlaza la variable $p a cada uno de estos valores ProductID por vez. Con cada valor de $p, la cláusula let explora la columna Items una vez y enlaza la variable $pc a una lista que contenga todos los comentarios cuyo ProductID coincida con el ProductID de $p. La cláusula return construye un nuevo elemento de producto para cada valor ProductID diferente. Cada uno de estos elementos de producto contiene dos subelementos: un elemento de IDq que contiene el valor de ProductID y un elemento de comentarios que contiene un conteo de la cantidad de comentarios recibidos por el producto.

El resultado de esta consulta de muestra será similar al mostrado en el Listado 23.

Listado 23. Datos de salida de muestra de la consulta anterior
<product> <id>3926</id>
                <comments>28</comments> </product>
                <product> <id>4097</id>
                <comments>13</comments> </product>

Consultas XQuery con SQL incrustado

Hasta ahora vimos cómo escribir consultas XQuery que recuperan fragmentos de documentos XML, cómo crear datos de salida XML con formatos nuevos y cómo devolver datos de salida diferentes basados en condiciones especificadas dentro de las consultas. En resumen, aprendimos algunas formas de uso de XQuery para consultar datos XML almacenados en DB2.

Queda, por supuesto, mucho más por aprender sobre XQuery que exceden los temas cubiertos por este breve artículo. Pero aún falta un tema amplio por desarrollar: cómo incrustar SQL dentro de XQuery. Esto puede resultar útil si necesitamos escribir consultas que filtren datos basados en valores XML y en valores de columnas no XML.

En la Parte 3 de esta serie vimos cómo incrustar expresiones XQuery simples dentro de una instrucción SQL simple para realizar dicha tarea. Aquí haremos lo contrario: incrustaremos SQL dentro de XQuery para limitar los resultados en base a valores de datos SQL tradicionales y a valores de elementos XML específicos.

En lugar de llamar a la función db2-fn:xmlcolumn, la cual devuelve todos los datos XML en una columna de tabla, podemos llamar a la función db2-fn:sqlquery, la cual ejecuta una consulta SQL y devuelve únicamente los datos seleccionados. La consulta SQL transmitida a db2-fn:sqlquery debe devolver datos XML. XQuery luego podrá continuar procesando estos datos XML.

La consulta del Listado 24 recupera información de comentarios relacionados con productos con un precio minorista sugerido (suggested retail price - srp) mayor a $100 USD que incluyen una solicitud de respuesta del cliente. Recordemos que los datos de precios se encuentran almacenados en una columna decimal SQL, mientras que los comentarios de clientes se almacenan como XML. Los datos devueltos (el ID de producto, el ID de cliente y el mensaje del cliente) se incluyen en un único elemento Action XML por cada comentario que califica almacenado en la base de datos.

Listado 24. Incrustación de SQL dentro de una consulta XQuery
xquery for $y in db2-fn:sqlquery('select comments from items where
                srp > 100')/Comments/Comment where $y/ResponseRequested="Yes" return (
                <action> {$y/ProductID, $y/CustomerID, $y/Message}
                </action> )

La mayor parte de esta consulta le resultará familiar, por lo cual nos concentraremos en la función nueva: db2-fn:sqlquery. DB2 procesa la instrucción SQL SELECT proporcionada a la función para que determine las filas que contienen información de artículos con precios mayores a $100 USD. Los documentos almacenados en estas filas serán tomados como datos de entrada por la expresión de ruta que devuelve todos los elementos incrustados de Comment. Los siguientes fragmentos de la consulta usan la cláusula where de XQuery para continuar filtrando los datos devueltos y transformar fragmentos de los comentarios seleccionados en nuevos fragmentos XML.

Tomando en cuenta lo anterior, piense cómo resolvería otro problema un poco diferente. Imagínese que deseamos obtener una lista de todas las direcciones de e-mail de clientes Gold que vivan en San José. Además, un único cliente tiene direcciones de e-mail múltiples y buscamos que todas ellas aparezcan en los datos de salida en un único registro de cliente. Por último, si algún cliente Gold que califica no proporcionó una dirección de e-mail, necesitaremos que se recuperen sus datos de correo postal. El Listado 25 muestra una forma de escribir una consulta que cumpla con estos requisitos:

Listado 25. Incrustación de SQL dentro de una consulta XQuery con lógica condicional
xquery for $y in db2-fn:sqlquery('select contactinfo from clients
                where status=''Gold'' ')
                /Client where $y/Address/city="San Jose"
                return 
                ( if
                ($y/email) then <emailList>{$y/email}</emailList> else
                $y/Address )

En esta consulta hay dos aspectos que requieren de explicación. En primer lugar, la instrucción SELECT incrustada en la segunda línea contiene un predicado de consulta basado en la columna Status que compara el valor de esta columna VARCHAR con la cadena Gold. En SQL, dichas cadenas se encuentran entre comillas simples. Tome en cuenta que, si bien el ejemplo a simple vista parece usar comillas dobles, en realidad usa dos comillas simples colocadas antes y después del valor a comparar (''Gold''). Las comillas simples adicionales son caracteres de escape. Si intentásemos usar comillas dobles antes y después del predicado de la consulta basada en cadenas en lugar de usar pares de comillas dobles, obtendríamos un error de sintaxis.

Por otra parte, la cláusula return de esta consulta contiene lógica condicional para determinar si un elemento de e-mail se encuentra presente dentro de un determinado registro de cliente. De ser así, la consulta devolverá un elemento emailList que contendrá todas las direcciones de e-mail del cliente (es decir, todos los elementos de e-mail de dicho cliente). De lo contrario, la consulta devolverá la dirección de correo postal del cliente (es decir, el elemento Address de dicho cliente).

Indización

Es importante tomar en cuenta que se pueden crear índices XML especializados para acelerar el acceso a los datos almacenados en columnas XML. Este tema excede el alcance de este artículo, que es simplemente una introducción con datos de muestra de tamaño reducido. Sin embargo, en los entornos de producción, la correcta definición de índices puede ser un factor esencial para lograr un rendimiento óptimo. Consulte la sección Recursos para obtener más información sobre la tecnología de indización de DB2.


Resumen

XQuery se diferencia de SQL en varios aspectos esenciales, muchos de los cuales se señalaron en este artículo. Un mayor conocimiento del lenguaje le ayudará a determinar cuándo podrá obtener más beneficios con su uso y a comprender cuándo puede resultar útil la combinación de XQuery con SQL. Otros artículos de esta serie explican cómo desarrollar aplicaciones Java que exploten las capacidades XML en DB2. De todas formas, este artículo incluye un ejemplo simple de Java que muestra cómo incrustar una consulta XQuery en una aplicación Java.

Agradecimientos

Quisiera agradecer a George Lapis, Matthias Nicola y Gary Robinson por revisar este artículo.

Recursos

Aprender

Obtener los productos y tecnologías

  • Construya su próximo proyecto de desarrollo con software de prueba de IBM, disponible para su descarga directa desde developerWorks.
  • Ahora puede usar DB2 gratis. Descargue DB2 Express-C, versión sin cargo de DB2 Express Edition para la comunidad que ofrece las mismas características de datos centrales que DB2 Express Edition y proporciona un sólida base para la construcción e implementación de aplicaciones.

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Information mgmt, Rational
ArticleID=487569
ArticleTitle=Comience a usar pureXML en DB2 V9 ya mismo, Parte 4: Consulta de datos XML en DB2 con XQuery
publish-date=05052010