Conjuntos

Operaciones de conjuntos y MySQL

Un operador sobre conjuntos combina el resultado de dos sentencias select en un único resultado. Dependiendo del tipo de operación, esas sentencias deberán cumplir unos requisitos en cuanto al resultado que dan. Los operadores de conjuntos definidos para el álgebra relacional, base sobre la que se cimenta SQL, son

  • unión
  • intersección
  • diferencia
  • producto cartesiano
  • división

No todos estarán necesariamente implementados. En MySQL solo está disponible el operador UNION, aunque la operativa de los otros puede simularse con otros operadores de los que ya hemos visto unos cuantos. En otros motores de base de datos se dispone de UNION, INTERSECT y MINUS.

Unión

Al utilizar el operador UNION entre dos sentencias select, el resultado final estará compuesto por todas aquellas filas que aparecen en el resultado de como mínimo una de las select. El operador UNION elimina filas duplicadas en el resultado final. El operador UNION ALL opera de igual modo que el operador UNION, pero no elimina filas duplicadas en el resultado final.

Supongamos que queremos saber el nombre de los profesores que son "ASO6" o imparten asignaturas de 6 créditos. Veamos primero el resultado de cada consulta por separado.

1) Nombre de los profesores cuya categoría es ASO6.

select nombre from profesores where categoria='ASO6';

2) Nombre de los profesores que imparten asignaturas de 6 créditos.

select nombre
from profesores p, imparte i, asignaturas
where p.dni=i.dni and asignatura=codigo and creditos=6;

Nombre de los profesores que son ASO6 o imparten asignaturas de 6 créditos.

select nombre from profesores where categoria='ASO6'
UNION
select nombre
from profesores p, imparte i, asignaturas
where p.dni=i.dni and asignatura=codigo and creditos=6;

La misma consulta pero solucionada con UNION ALL

select nombre from profesores where categoria='ASO6'
UNION ALL
select nombre
from profesores p, imparte i, asignaturas
where p.dni=i.dni and asignatura=codigo and creditos=6;

Intersección

Aunque no existe tal operador en MySQL, normalmente se puede sustituir por consultas ya conocidas.

DNI de los profesores que imparten y preparan: IMPARTE[dni] (COORDINADORES[dni])

Lo que debería solucionarse como

select dni from imparte
INTERSECT
select dni from prepara;

En realidad, en MySQL, debemos expresarlo como:

select distinct i.dni
from imparte i, prepara pp
where pp.dni=i.dni;

Se usa el modificador distinct para que la expresión sustituya realmente la operativa de la intersección: las operaciones de conjuntos no devuelven duplicados —al igual que union all es una extensión para que se puedan resolver cierto tipo de consultas que sí necesitan los duplicados—. No obstante, lo usaremos o no dependiendo de si lo necesitamos o no.

Diferencia

El operador MINUS tampoco está implementado en MySQL pero es fácilmente sustituible por expresiones NOT IN. En todo caso, el resultado final estará compuesto sólo por aquellas filas que aparecen en el resultado de la primera select y no aparecen en el resultado de la segunda.

Dni y nombre de los profesores que son TEU y no imparten asignaturas de 6 créditos.

PROFESORES donde categoría='TEU' [dni,nombre]
-
(PROFESORES x IMPARTE x ASIGNATURA
donde (PROFESORES.dni = IMPARTE.dni y codigo=asignatura y créditos=6)
[PROFESORES.dni,nombre])

Lo que debería solucionarse como

select dni,nombre from profesores where categoria='TEU'
MINUS 
select p.dni,nombre 
from profesores p, imparte i, asignaturas 
where p.dni=i.dni and asignatura=codigo and creditos=6; 

al no existir ese operador en MySQL, utilizamos NOT IN para simularlo:

select dni,nombre from profesores where categoria='TEU'
and dni NOT IN 
(select p.dni
 from profesores p, imparte i, asignaturas
 where p.dni=i.dni and asignatura=codigo and creditos=6);

Esta sería la consulta exactamente equivalente a la diferencia de conjuntos tal y como está definida en el álgebra relacional, con relaciones compatibles, pero estamos trabajando con SQL, podemos simplificar la consulta y obtendremos el mismo resultado:

select nombre from profesores 
where categoria='TEU'
  and dni NOT IN 
(select dni
 from imparte i, asignaturas
 where asignatura=codigo and creditos=6);

Producto cartesiano

En SQL, y como ya se ha dicho anteriormente, el producto cartesiano se realiza con una select "sin where".

select dni, codigo 
from profesores, asignaturas 

No obstante, el producto cartesiano, como cualquier otro tipo de consulta, puede filtrar las filas resultado a nuestra conveniencia.

Dni de los profesores que imparten 2 o más asignaturas

select distinct i1.dni
from imparte i1, imparte i2
where i1.dni = i2.dni 
    and i1.asignatura != i2.asignatura; 

Del producto cartesiano de una tabla por sí misma solo nos interesan las filas en las que el profesor "de la izquierda" es el mismo que el "de la derecha"; sí, además, las asignaturas son diferentes, la conclusión no puede ser otra que ese profesor cumple con la condición.

Aunque para el ejemplo que hemos propuesto hay una forma más amigable de solucionar este enunciado, la que mostramos a continuación, el producto cartesiano es una opción que podemos utilizar cuando creamos conveniente

select dni from imparte group by dni having count(*) >= 2;

Nombre de los profesores que imparten asignaturas de 6 créditos y no son TEU.

En álgebra relacional se solucionaría de la siguiente manera:

PROFESORES x IMPARTE x ASIGNATURA
donde (PROFESORES.dni = IMPARTE.dni y codigo=asignatura y créditos=6)
[nombre]
-
(PROFESORES donde categoría='TEU' [nombre])

Y en SQL:

select nombre
from profesores p, imparte i, asignaturas
where p.dni=i.dni and asignatura=codigo and creditos=6
and nombre NOT IN 
(select nombre from profesores where categoria='TEU');

Concatenación natural

Este operador está implementado en MySQL y Oracle Database pero no es seguro que otros SGBD lo tengan implementado igualmente. Igual que en álgebra relacional, este operador asume que hay columnas comunes en dos tablas diferentes (en principio, que se llamen igual) y automatiza la reunión (join) en base a la igualdad de valores en estas columnas comunes. Dicho de otra forma:

Todos los datos de profesores que imparten alguna asignatura y códigos de esas asignaturas.

select p.dni, p.nombre, p.categoria, p.ingreso, i.asignatura
from profesores p, imparte i
where p.dni=i.dni;

Se obtiene el mismo resultado que con

select * from profesores NATURAL JOIN imparte;

En ambos casos la salida es la que se muestra a continuación. Nótese que solo se muestra una columna de "dni" (concretamente la de PROFESORES, la tabla a la izquierda del operador).

Su expresión en álgebra relacional sería:

PROFESORES ∞ IMPARTE

A pesar de la posible comodidad de uso de este operador hay que tener mucho cuidado, si no existen columnas comunes se devolverá un producto cartesiano. Otra fuente de resultados no esperados es que haya más columnas con idéntico nombre de las deseadas.

Material adicional

Aunque es un operador que se comporta de manera diferente a los anteriores también podríamos incluir EXISTS dentro de la categoría de operadores SQL de conjuntos. Además, es la base a una de las simulaciones clásicas del operador DIVISIÓN del álgebra relacional. Ambos se comentan en una lección complementaria.