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.
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.
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> |
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)
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).
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últipleswhere: 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:
- 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.
- 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
returnindica 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> . . . |
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.
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).
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.
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.
Quisiera agradecer a George Lapis, Matthias Nicola y Gary Robinson por revisar este artículo.
Aprender
- Sitio Web de
pureXML en DB2: Conozca más acerca del soporte de XML que ofrece
DB2.
- "Get off to a fast start with DB2 V9 pureXML, Part 1" (developerWorks,
febrero de 2006, actualizado en marzo de 2010): Conozca generalidades de las nuevas
tecnologías XML que ahora se proporcionan en beta en DB2.
- "Get off to a fast start with DB2 V9, Part 2" (developerWorks, marzo de
2006, actualizado en marzo de 2010): Aprenda a insertar, importar y validar datos
XML.
- "Get off to a fast start with DB2 V9, Part 3: Query XML Data in DB2 with
SQL" (developerWorks, March 2006, actualizado en marzo de 2010): Aprenda a
consultar datos XML en DB2 usando SQL y SQL/XML.
- XQuery from the Experts, (Howard Katz, et. al.,
Addison-Wesley, 2003): Conozca el lenguaje XQuery. Encuentre un extracto de
este libro online.
- "Firing up the Hybrid Engine" (DB2 Magazine, Tercer trimestre, 2005):
Lea más acerca del sistema de gestión de base de datos híbrido de IBM.
- System RX: One Part
Relational, One Part XML(Conferencia SIGMOD 2005): Conozca los aspectos de
arquitectura y diseño de la construcción de un DBMS relacional híbrido y
XML.
- "Native XML
Support in DB2 Universal Database" (Conferencia VLDB, 2005): Conozca más
acerca del soporte de XML en DB2.
- "Managing XML for Maximum Return" (IBM, noviembre de 2005): Este white paper
analiza los beneficios de negocios del soporte de XML en DB2.
- developerWorks Information Management zone: Encuentre más recursos para
desarrolladores y administradores de DB2 UDB.
- Manténgase al día con los eventos técnicos
o transmisiones por Internet de developerWorks.
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
- Participar en el foro de debate.
- Blogs developerWorks: participe en la comunidad developerWorks.

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