Conjuntos
Operaciones de conjuntos y MariaDB/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
concatenación natural
No todos estarán implementados. En MariaDB/MySQL solo están disponibles los operadores UNION y NATURAL JOIN, aunque la operativa de los otros puede emularse. En otros motores de base de datos se pueden encontrar INTERSECT y MINUS.
Producto cartesiano
En SQL, el producto cartesiano entre dos tablas se obtiene si no establecemos ninguna condición a evaluar en where. De hecho, proponemos pensar en cualquier consulta como una secuencia ordenada de operaciones, de las cuales from es la primera, y cuyo resultado es el producto cartesiano de todas las tablas especificadas. Los operadores en álgebra relacional serían el producto cartesiano, la selección y la proyección:
select nombre, codigo from PROFESORES, IMPARTE where PROFESORES.dni = IMPARTE.dni
PROFESORES x IMPARTE -- from
donde PROFESORES.dni = IMPARTE.dni -- where
[nombre, codigo] -- select
Todas las combinaciones posibles de dni de profesor y código de asignatura
En álgebra relacional se solucionaría con
PROFESORES x ASIGNATURA [dni, codigo]
O con
PROFESORES[dni] x (ASIGNATURA [codigo])
En SQL
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"; si, 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;
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, 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 MariaDB/MySQL, normalmente se puede sustituir por consultas ya conocidas.
DNI de los profesores que imparten y preparan: IMPARTE[dni] ∩ (PREPARA[dni])
Lo que debería solucionarse como
select dni from imparte
INTERSECT
select dni from prepara;
En realidad, en MariaDB/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 MariaDB/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. En algunos casos puede ser necesario o recomendable utilizar el modificador distinct.
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 MariaDB/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);
Concatenación natural
Este operador está implementado en MariaDB/MySQL y Oracle Database pero no es seguro que otros SGBD también lo tengan. 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. Además, NATURAL JOIN no produce filas duplicadas.
Todos los datos de profesores que imparten alguna asignatura y códigos de esas asignaturas.
select distinct 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 que se ofrece para quien quiera ampliar sus conocimientos de estos lenguajes.