Conjuntos adicional

El operador EXISTS

EXISTS es un operador de un único argumento y cuyo resultado es un valor de verdad, informa de la presencia o no de tuplas en una tabla.

[NOT] EXISTS (orden select)

El operador exists nos informa de si una subconsulta ha obtenido algún resultado: devuelve verdadero si hay al menos una tupla en la relación derivada y falso si la relación derivada es vacía.

¿Tenemos profesores?

select exists (select 1 from profesores) respuesta

Puesto que no necesitamos nada de la tabla profesores salvo saber si hay o no filas, por eso nos basta con "select 1": el resultado de la subconsulta sería tantas filas con "1" como filas haya en la tabla profesores.

Es más habitual componer una condición que nos permita filtrar filas.

Todos los datos de las asignaturas que son impartidas por algún profesor

select * from asignaturas a 
where exists (select 1 from imparte i where i.asignatura=a.codigo)


El resultado sería el mismo si pidiéramos

select * from asignaturas 
where codigo IN (select asignatura from imparte)

Eficiencia

Aunque para la cantidad de filas que estamos manejando tanto en la BD Ejemplo como en TiendaOnLine la diferencia es irrelevante, el uso de exists puede ser adecuado para mejorar el rendimiento de ciertas consultas. Sea, por ejemplo:

select distinct nombre
from profesores p, imparte i
where p.dni = i.dni;

En realidad, sólo nos interesa saber el nombre de los profesores que imparten alguna asignatura, sin reparar en cuáles son esas asignaturas. Para eso, es mejor utilizar:

select nombre from profesores p
where exists (select 1 from imparte where dni = p.dni); 

La ventaja de esta segunda consulta es que la subconsulta sólo necesita devolver una fila por cada profesor, mientras que la primera primero obtiene la concatenación de todos los códigos de asignatura con su profesor correspondiente y después proyecta sobre la columna nombre. Podemos suponer un estado de la base de datos con 1000 asignaturas, y que los 3 profesores imparten todas las asignaturas: la primera consulta trabajaría con 3000 filas, mientras que la segunda no necesitaría más de 3.

No obstante, lo dicho aquí debe considerarse como un apunte sobre la necesidad en ciertos entornos de mejorar el rendimiento de aquellas consultas que son críticas por su frecuencia o por un gran volumen de datos, esto es, "ayudar" al optimizador de consultas.

En el esquema de base de datos EJEMPLO tenemos una tabla PREPARA con la siguiente estructura

CREATE TABLE prepara (
 dni VARCHAR(10),
 asignatura CHAR(5),
 PRIMARY KEY (dni, asignatura),
 FOREIGN KEY (asignatura) REFERENCES asignaturas (codigo),
 FOREIGN KEY (dni) REFERENCES profesores (dni)
)ENGINE=InnoDB;

Esta tabla representa a profesores que han sido asignados para preparar los contenidos y materiales y evaluación de ciertas asignaturas. Es una relación muchos a muchos entre PROFESORES y ASIGNATURAS.

Nombre de los profesores que preparan alguna asignatura

select nombre from profesores p
where exists
   (select codigo from asignaturas a
    where exists
       (select 1 from prepara pp
        where pp.asignatura=a.codigo and p.dni=pp.dni)
   )

Podríamos leer esta consulta algo así como "dame el nombre de los profesores para los que existe alguna asignatura tal que también existe alguna fila en PREPARA que relaciona a ambos".

Lo cierto es que la consulta anterior se soluciona mucho más fácil como

select nombre
from profesores p, prepara pp
where p.dni=pp.dni 

En este caso resulta difícil argumentar en favor de la consulta con exists.

Pero si negamos solo uno de los exists

Nombre de los profesores que no preparan todas las asignaturas (que existe al menos una asignatura que no prepara)

select nombre from profesores p
where exists
   (select codigo from asignaturas a
    where not exists
       (select 1 from prepara pp
        where pp.asignatura=a.codigo and p.dni=pp.dni)
   )

Cuando esto se pone difícil es al emular el operador división del álgebra relacional.

Nombre de los profesores que preparan todas las asignaturas

select nombre from profesores p
where not exists
   (select codigo from asignaturas a
    where not exists
       (select 1 from prepara pp
        where pp.asignatura=a.codigo and p.dni=pp.dni)
   )

Digamos que viene a ser "dame el nombre de los profesores tales que no existe asignatura que no preparen ellos". Definitivamente, resulta complicado lidiar con dobles negaciones, pero no imposible.

Alternativas

A veces, el resultado buscado permite otro tipo de soluciones.

Nombre de los profesores que preparan todas las asignaturas.

select nombre from profesores p, prepara pp
where p.dni = pp.dni
group by p.dni, nombre
having count(*) = (select count(*) from asignaturas);

Nombre de los profesores que no preparan ninguna asignatura.

select nombre from profesores p
where not exists
(select codigo from asignaturas a
where exists
(select 1 from prepara pp
where pp.asignatura=a.codigo and p.dni=pp.dni)
);

Posiblemente de forma más intuitiva:

select nombre from profesores p
where not exists
(select 1 from prepara pp
where p.dni=pp.dni);

Aunque, realmente, la alternativa más clara y fácil de entender es esta:

select nombre from profesores
where dni NOT IN (select dni from prepara);