Anexo: Ejercicios de XQuery

En los ejercicios siguientes se asume que se va a utilizar la siguiente base de datos XML (tomada del libro de C.J. Date «Sistemas Gestores de Bases de Datos»)

<datos>
    <proveedores>
        <proveedor numprov="v1">
            <nombreprov>Smith</nombreprov>
            <estado>20</estado>
            <ciudad>Londres</ciudad>
        </proveedor>
        <proveedor numprov="v2">
            <nombreprov>Jones</nombreprov>
            <estado>10</estado>
            <ciudad>Paris</ciudad>
        </proveedor>
        <proveedor numprov="v3">
            <nombreprov>Blake</nombreprov>
            <estado>30</estado>
            <ciudad>Paris</ciudad>
        </proveedor>
        <proveedor numprov="v4">
            <nombreprov>Clarke</nombreprov>
            <estado>20</estado>
            <ciudad>Londres</ciudad>
        </proveedor>
        <proveedor numprov="v5">
            <nombreprov>Adams</nombreprov>
            <estado>30</estado>
            <ciudad>Atenas</ciudad>
        </proveedor>       
    </proveedores>
    <partes>
        <parte numparte="p1">
            <nombreparte>Tuerca</nombreparte>
            <color>Rojo</color>
            <peso>12</peso>
            <ciudad>Londres</ciudad>
        </parte>
        <parte numparte="p2">
            <nombreparte>Perno</nombreparte>
            <color>Verde</color>
            <peso>17</peso>
            <ciudad>Paris</ciudad>
        </parte>
        <parte numparte="p3">
            <nombreparte>Tornillo</nombreparte>
            <color>Azul</color>
            <peso>17</peso>
            <ciudad>Roma</ciudad>
        </parte>
        <parte numparte="p4">
            <nombreparte>Tornillo</nombreparte>
            <color>Rojo</color>
            <peso>14</peso>
            <ciudad>Londres</ciudad>
        </parte>
        <parte numparte="p5">
            <nombreparte>Leva</nombreparte>
            <color>Azul</color>
            <peso>12</peso>
            <ciudad>Paris</ciudad>
        </parte>
        <parte numparte="p6">
            <nombreparte>Engranaje</nombreparte>
            <color>Rojo</color>
            <peso>19</peso>
            <ciudad>Londres</ciudad>
        </parte>
    </partes>
    <proyectos>
        <proyecto numproyecto="y1">
            <nombreproyecto>Clasificador</nombreproyecto>
            <ciudad>Paris</ciudad>
        </proyecto>
        <proyecto numproyecto="y2">
            <nombreproyecto>Monitor</nombreproyecto>
            <ciudad>Roma</ciudad>
        </proyecto>
        <proyecto numproyecto="y3">
            <nombreproyecto>OCR</nombreproyecto>
            <ciudad>Atenas</ciudad>
        </proyecto>
        <proyecto numproyecto="y4">
            <nombreproyecto>Consola</nombreproyecto>
            <ciudad>Atenas</ciudad>
        </proyecto>
        <proyecto numproyecto="y5">
            <nombreproyecto>RAID</nombreproyecto>
            <ciudad>Londres</ciudad>
        </proyecto>
        <proyecto numproyecto="y6">
            <nombreproyecto>EDS</nombreproyecto>
            <ciudad>Oslo</ciudad>
        </proyecto>
        <proyecto numproyecto="y7">
            <nombreproyecto>Cinta</nombreproyecto>
            <ciudad>Londres</ciudad>
        </proyecto>
    </proyectos>
    <suministros>
        <suministra>
            <numprov>v1</numprov>
            <numparte>p1</numparte>
            <numproyecto>y1</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v1</numprov>
            <numparte>p1</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>700</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y1</numproyecto>
            <cantidad>400</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y2</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y3</numproyecto>
            <cantidad>300</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>500</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y5</numproyecto>
            <cantidad>600</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y6</numproyecto>
            <cantidad>400</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p3</numparte>
            <numproyecto>y7</numproyecto>
            <cantidad>600</cantidad>
        </suministra>
        <suministra>
            <numprov>v2</numprov>
            <numparte>p5</numparte>
            <numproyecto>y2</numproyecto>
            <cantidad>100</cantidad>
        </suministra>
        <suministra>
            <numprov>v3</numprov>
            <numparte>p3</numparte>
            <numproyecto>y1</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v3</numprov>
            <numparte>p4</numparte>
            <numproyecto>y2</numproyecto>
            <cantidad>500</cantidad>
        </suministra>
        <suministra>
            <numprov>v4</numprov>
            <numparte>p6</numparte>
            <numproyecto>y3</numproyecto>
            <cantidad>300</cantidad>
        </suministra>
        <suministra>
            <numprov>v4</numprov>
            <numparte>p6</numparte>
            <numproyecto>y7</numproyecto>
            <cantidad>300</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p2</numparte>
            <numproyecto>y2</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p2</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>100</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p5</numparte>
            <numproyecto>y5</numproyecto>
            <cantidad>500</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p6</numparte>
            <numproyecto>y2</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p1</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>100</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p3</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>200</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p4</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>800</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p5</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>400</cantidad>
        </suministra>
        <suministra>
            <numprov>v5</numprov>
            <numparte>p6</numparte>
            <numproyecto>y4</numproyecto>
            <cantidad>500</cantidad>
        </suministra>
    </suministros>
</datos>

A continuación se muestra la estructura en forma de tabla de los elementos XML de dicho archivo. Obsérvese que los atributos llevan la arroba delante y que no se han puesto todas las filas:

Tabla proveedores

@numprov

nombre

estado

ciudad

v1

Smith

20

Londres

v2

Jones

10

Paris

Tabla partes:

@numparte

nombreparte

color

peso

ciudad

p1

Tuerca

Rojo

12

Londres

p2

Perno

Verde

17

Paris

Tabla proyectos

@numproyecto

nombreproyecto

ciudad

y1

Clasificador

Paris

y2

Monitor

Roma

Tabla suministra

numprov

numparte

numproyecto

cantidad

v1

p1

y1

200

v1

p1

y4

700

Obsérvese también que:

  • En la tabla suministra el campo numprov es el mismo que el campo numprov de la tabla proveedor

  • En la tabla suministra el campo numparte es el mismo que el campo numparte de la tabla partes

  • En la tabla suministra el campo numproyecto es el mismo que el campo numproyecto de la tabla proyectos

Consulta: ciudad de los proveedores

Extraer la ciudad de los proveedores (no debe aparecer la etiqueta) que tengan un estado mayor de 15.

for $proveedor in doc("datos.xml")/datos/proveedores/proveedor
where $proveedor/estado > 15
return $proveedor/ciudad/text()

Si se ejecuta esto se verá que el resultado es correcto sin embargo la presentación no es muy buena, al mostrarse todo seguido. Usemos por ejemplo la función concat para que cada resultado lleve un espacio detrás:

for $proveedor in doc("datos.xml")/datos/proveedores/proveedor
where $proveedor/estado > 15
return concat($proveedor/ciudad/text(), ' ')

Consulta: filas de la tabla partes

Averiguar cuantas partes existen, es decir, el total de filas de la «tabla» partes.

Para resolverlo una tentación muy común es resolverlo así:

for $partes in doc("datos.xml")/datos/partes
return count ($partes/parte)

Y aunque esta solución funciona en realidad estamos haciendo un bucle de una sola iteración.

En este caso, se puede recurrir directamente a la función count que permite contar el número de elementos de una consulta parcial sin tener que hacer siquiera el recorrido.

count (doc("datos.xml")/datos/partes/parte)

Consulta con join’s

Obtener el nombre de los proyectos cuya ciudad sea Paris y que reciban una cantidad de partes > 350

Paso 0: análisis

La cantidad está en las filas (elementos XML) suministra pero el nombreproyecto está en las filas proyecto. Será necesario «cruzar» elementos proyecto con elementos suministra usando como condición que @numproyecto de los elementos proyecto sea igual a los campos numproyecto de los elementos suministra

Paso 1: hacemos el cruce

Una primera aproximación sería esta:

for $suministra in
  doc("datos.xml")/datos/suministros/suministra
for $proyecto in
  doc("datos.xml")/datos/proyectos/proyecto
where $suministra/numproyecto = $proyecto/@numproyecto

return $proyecto/nombreproyecto

Sin embargo, esto devuelve «todos los proyectos» que de alguna manera aparezcan en la tabla suministra

Paso 2: añadir condiciones

Nos han dicho que la cantidad de la tabla suministra debe ser mayor de 350, así que en el where o en el for de suministra podemos añadir una condición de filtrado:

Asimismo necesitamos solamente los proyectos cuyo campo ciudad sea Paris.

for $suministra in
  doc("datos.xml")/datos/suministros/suministra[cantidad>350]
for $proyecto in
  doc("datos.xml")/datos/proyectos/proyecto[ciudad="Paris"]
where $suministra/numproyecto = $proyecto/@numproyecto

return $proyecto/nombreproyecto

Otra variante usando condiciones en el where sería esta:

for $suministra in
  doc("datos.xml")/datos/suministros/suministra
for $proyecto in
  doc("datos.xml")/datos/proyectos/proyecto
where $suministra/numproyecto = $proyecto/@numproyecto
   and
     $suministra/cantidad > 350
   and
     $proyecto/ciudad="Paris"
return $proyecto/nombreproyecto

Consulta: ciudades iguales

Obtener los nombres de proyecto y nombres de parte que estén en la misma ciudad.

for $proyecto in
    doc("datos.xml")/datos/proyectos/proyecto
    for $parte in
        doc("datos.xml")/datos/partes/parte
        where
            $parte/ciudad = $proyecto/ciudad
    return concat(
        $parte/nombreparte, " en la misma ciudad que ",
        $proyecto/nombreproyecto, "-----"
    )

Consulta: partes con colores iguales

Obtener parejas de partes que tengan el mismo color (indicando el nombre de ambas partes y el color que comparten)

for $p1 in
    doc("datos.xml")/datos/partes/parte
for $p2 in
    doc("datos.xml")/datos/partes/parte
    where
        $p1/color = $p2/color
    return concat ($p1/nombreparte,
                   " tiene el mismo color que ",
                   $p2/nombreparte,
                   " en concreto el color es:",
                   $p1/color, "
                   ")

Esta consulta funciona, pero ofrece parejas de partes que no tienen mucho sentido en pantalla, por ejemplo «Tuerca es igual que Tuerca». Para mejorar la consulta, vamos a eliminar parejas en la cuales el numparte sea el mismo, es decir no vamos a contemplar el emparejar una parte consigo misma.

for $p1 in
    doc("datos.xml")/datos/partes/parte
for $p2 in
    doc("datos.xml")/datos/partes/parte
where
    $p1/color = $p2/color
and
    $p1/@numparte != $p2/@numparte
return concat ($p1/nombreparte,
                   " tiene el mismo color que ",
                   $p2/nombreparte,
                   " en concreto el color es:",
                   $p1/color, "
                   ")

Consulta: cantidad de partes de Londres

Averiguar cuantas partes existen cuya ciudad sea «Londres», es decir, el total de filas de la «tabla» partes pero teniendo en cuenta la condición de que el «campo» ciudad debe ser Londres.

count (doc("datos.xml")/datos/partes/parte[ciudad='Londres'])

Consulta: media de partes rojas

Crear una consulta XQuery que averigüe la media de partes suministradas cuyo color sea “Rojo”

Paso 1: cruce de tablas

for $parte in
    doc("datos.xml")/datos/partes/parte
for $suministra in
    doc("datos.xml")/datos/suministros/suministra
where
    $parte/@numparte = $suministra/numparte
//Faltaria el return

Paso 2: añadir condicion de filtrado

for $parte in
    doc("datos.xml")/datos/partes/parte
for $suministra in
    doc("datos.xml")/datos/suministros/suministra
where
    $parte/@numparte = $suministra/numparte
and
    $parte/color='Rojo'
return $suministra/cantidad

En realidad, este filtro también se podría hacer así:

for $parte in
    doc("datos.xml")/datos/partes/parte[color='Rojo']
for $suministra in
    doc("datos.xml")/datos/suministros/suministra
where
    $parte/@numparte = $suministra/numparte
return $suministra/cantidad

Paso 3: calcular la media

avg (
    for $parte in
        doc("datos.xml")/datos/partes/parte[color='Rojo']
    for $suministra in
        doc("datos.xml")/datos/suministros/suministra
    where
        $parte/@numparte = $suministra/numparte
    return $suministra/cantidad
)

Comprobación

Si analizamos la tabla partes veremos que las únicas partes cuyo color es “Rojo” son las partes p1, p4 y p6. Esto significa que las unicas filas de suministra que nos interesan son estas:

numprov

numparte

numproyecto

cantidad

v1

p1

y1

200

v1

p1

y4

700

v3

p4

y2

500

v4

p6

y3

300

v4

p6

y7

300

v5

p6

y2

200

v5

p1

y4

100

v5

p4

y4

800

v5

p6

y4

500

Como vemos hay 9 filas con suministros de partes cuyo color es “Rojo” y la suma de cantidades es 3600 por lo el resultado correcto es 400

Consulta: media individual de partes rojas

En el ejercicio anterior hemos calculado la media global de partes rojas. Sin embargo, nos interesaría conocer la media de cada parte roja. Es decir, la media de p1, la media de p4 y la media de p6.

Extraigamos primero las partes cuyo color es rojo:

for $parte in doc("datos.xml")/datos/partes/parte[color='Rojo']
return $parte

Si analizamos los resultados veremos que nos devuelve las partes p1, p4 y p6. Una vez hecho esto ahora devolvamos la media para cada una de esas partes:

for $parte in doc("datos.xml")/datos/partes/parte[color='Rojo']
return avg( doc("datos.xml")/datos/suministros/suministra
    [numparte=$parte/@numparte]/cantidad)

Esta consulta nos muestra solo las cantidades de cada parte cuyo color es rojo. Vamos a mejorarla haciendo que aparezcan los nombres de parte. Obsérvese que concatenamos las siguientes cosas:

  • Primero el nombre de la parte.

  • Segundo un pequeño separador.

  • Despues la media (que se calcula solo para las filas de suministra cuyo numparte es igual que el atributo numparte de la parte que estamos analizando.)

  • Despues un espacio que separa unos resultados de otros

for $parte in doc("datos.xml")/datos/partes/parte[color='Rojo']
return concat (
    $parte/nombreparte,
    "--",
    avg (
        doc("datos.xml")/datos/suministros/suministra
        [numparte=$parte/@numparte]/cantidad
        ), "          " )

Consulta: media de suministros

Averiguar la media de la cantidad de partes que aparecen en la «tabla» suministra

avg (doc("datos.xml")/datos/suministros/suministra/cantidad)

Pregunta: ¿por qué no ponemos esta solución?

avg (doc("datos.xml")/datos/suministros/suministra/cantidad/text())

Respuesta: las funciones son capaces de «extraer el texto automáticamente» si en el elemento no hay hijos. En este caso, la cantidad no tiene hijos, por lo que podemos ahorrarnos el text()

Consulta: media por proveedor

Averiguar la media de cantidades por proveedor usando los datos de la tabla suministra.

Hagamos esta consulta por partes. En primer lugar, saquemos los distintos proveedores que hay en suministra

for $n in distinct-values(
doc("datos.xml")/datos/
proveedores/proveedor/@numprov)
return $n

Ahora, teniendo los distintos proveedores podemos devolver algo distinto de numprov. Podemos devolver la media para ese proveedor aprovechando los filtrados XPath.

for $n in distinct-values(
doc("datos.xml")/datos/
proveedores/proveedor/@numprov)
return avg
( doc("datos.xml")/datos/suministros/suministra
[numprov=$n]/cantidad)

Y por último, si usamos concat apropiadamente podemos hacer que aparezca el número de proveedor al lado de dica cantidad.

for $n in distinct-values(
doc("datos.xml")/datos/proveedores/proveedor/@numprov)
return concat (
 $n, ' ', avg(
 doc("datos.xml")/datos/suministros
/suministra[numprov=$n]/cantidad)
)

Consulta: suministros en grandes cantidades

Averiguar el nombre de los proyectos (sin que haya repeticiones) que reciban una cantidad en la tabla suministra mayor de 650.

for $proyecto
in doc("datos.xml")/datos/proyectos/proyecto
for $suministra
in
doc("datos.xml")/datos/suministros/suministra[cantidad>650]
where $proyecto/@numproyecto = $suministra/numproyecto
return ($proyecto/nombreproyecto, $suministra/cantidad)