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 

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.