Estoy tratando de aprender SQL y me cuesta entender las declaraciones EXISTS. Encontré esta cita sobre "existe" y no entiendo algo:
Usando el operador existe, su subconsulta puede devolver cero, una o muchas filas, y la condición simplemente verifica si la subconsulta devolvió alguna fila. Si observa la cláusula select de la subconsulta, verá que consta de un solo literal (1); Dado que la condición en la consulta contenedora solo necesita saber cuántas filas se han devuelto, los datos reales que devolvió la subconsulta son irrelevantes.
Lo que no entiendo es cómo la consulta externa sabe qué fila está verificando la subconsulta. Por ejemplo:
SELECT *
FROM suppliers
WHERE EXISTS (select *
from orders
where suppliers.supplier_id = orders.supplier_id);
Entiendo que si la identificación del proveedor y la tabla de pedidos coinciden, la subconsulta devolverá verdadero y se mostrarán todas las columnas de la fila correspondiente en la tabla de proveedores. Lo que no entiendo es cómo la subconsulta comunica qué fila específica (digamos, la fila con la identificación del proveedor 25) debe imprimirse si solo se devuelve verdadero o falso.
Me parece que no hay relación entre la consulta externa y la subconsulta.
¿Qué crees que está haciendo la cláusula WHERE dentro del ejemplo EXISTS? ¿Cómo se llega a esa conclusión cuando la referencia SUPPLIERS no está en las cláusulas FROM o JOIN dentro de la cláusula EXISTS?
EXISTS valora VERDADERO / FALSO y sale como VERDADERO en la primera coincidencia de los criterios; por eso puede ser más rápido que
IN
. También tenga en cuenta que la cláusula SELECT en un EXISTS se ignora - IE:SELECT s.* FROM SUPPLIERS s WHERE EXISTS (SELECT 1/0 FROM ORDERS o WHERE o.supplier_id = s.supplier_id)
... debería llegar a una división por error cero, pero no lo hará. La cláusula WHERE es la parte más importante de una cláusula EXISTS.
También tenga en cuenta que JOIN no es un reemplazo directo de EXISTS, porque habrá registros principales duplicados si hay más de un registro secundario asociado al principal.
fuente
EXISTS
salidas, devolviendo VERDADERO en la primera coincidencia, porque el proveedor existe al menos una vez en la tabla PEDIDOS. Si quisiera ver la duplicación de los datos del PROVEEDOR debido a que tiene más de una relación secundaria en PEDIDOS, tendría que usar JOIN. Pero la mayoría no quiere esa duplicación, y la ejecución de GROUP BY / DISTINCT puede agregar una sobrecarga a la consulta.EXISTS
es más eficiente queSELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...
en SQL Server, no he probado en Oracle o MySQL últimamente.Puede producir resultados idénticos usando
JOIN
,EXISTS
,IN
, oINTERSECT
:SELECT s.supplier_id FROM suppliers s INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o ON o.supplier_id = s.supplier_id SELECT s.supplier_id FROM suppliers s WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id) SELECT s.supplier_id FROM suppliers s WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o) SELECT s.supplier_id FROM suppliers s INTERSECT SELECT o.supplier_id FROM orders o
fuente
Si tuviera una cláusula where que se pareciera a esto:
WHERE id in (25,26,27) -- and so on
puede comprender fácilmente por qué se devuelven algunas filas y otras no.
Cuando la cláusula where es así:
WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);
simplemente significa: devolver filas que tienen un registro existente en la tabla de pedidos con la misma identificación.
fuente
Modelo de tabla de base de datos
Supongamos que tenemos las siguientes dos tablas en nuestra base de datos, que forman una relación de tabla de uno a muchos.
La
student
tabla es la principal y lastudent_grade
tabla secundaria, ya que tiene una columna de clave externa student_id que hace referencia a la columna de clave principal id en la tabla de estudiantes.El
student table
contiene los siguientes dos registros:| id | first_name | last_name | admission_score | |----|------------|-----------|-----------------| | 1 | Alice | Smith | 8.95 | | 2 | Bob | Johnson | 8.75 |
Y la
student_grade
tabla almacena las calificaciones que recibieron los estudiantes:| id | class_name | grade | student_id | |----|------------|-------|------------| | 1 | Math | 10 | 1 | | 2 | Math | 9.5 | 1 | | 3 | Math | 9.75 | 1 | | 4 | Science | 9.5 | 1 | | 5 | Science | 9 | 1 | | 6 | Science | 9.25 | 1 | | 7 | Math | 8.5 | 2 | | 8 | Math | 9.5 | 2 | | 9 | Math | 9 | 2 | | 10 | Science | 10 | 2 | | 11 | Science | 9.4 | 2 |
SQL EXISTE
Digamos que queremos que todos los estudiantes que hayan recibido una calificación de 10 en la clase de matemáticas.
Si solo estamos interesados en el identificador del estudiante, entonces podemos ejecutar una consulta como esta:
SELECT student_grade.student_id FROM student_grade WHERE student_grade.grade = 10 AND student_grade.class_name = 'Math' ORDER BY student_grade.student_id
Pero, la aplicación está interesada en mostrar el nombre completo de a
student
, no solo el identificador, por lo que también necesitamos información de lastudent
tabla.Para filtrar los
student
registros que tienen una nota de 10 en Matemáticas, podemos usar el operador EXISTS SQL, así:SELECT id, first_name, last_name FROM student WHERE EXISTS ( SELECT 1 FROM student_grade WHERE student_grade.student_id = student.id AND student_grade.grade = 10 AND student_grade.class_name = 'Math' ) ORDER BY id
Al ejecutar la consulta anterior, podemos ver que solo se selecciona la fila Alice:
| id | first_name | last_name | |----|------------|-----------| | 1 | Alice | Smith |
La consulta externa selecciona las
student
columnas de fila que estamos interesados en devolver al cliente. Sin embargo, la cláusula WHERE utiliza el operador EXISTS con una subconsulta interna asociada.El operador EXISTS devuelve verdadero si la subconsulta devuelve al menos un registro y falso si no se selecciona ninguna fila. El motor de la base de datos no tiene que ejecutar la subconsulta por completo. Si un solo registro coincide, el operador EXISTS devuelve verdadero y se selecciona la otra fila de consulta asociada.
La subconsulta interna está correlacionada porque la columna student_id de la
student_grade
tabla se compara con la columna id de la tabla externa de estudiantes.fuente
EXIST
Solo funciona con subconsultas correlacionadas? Estaba jugando con una consulta que contenía solo 1 tabla, comoSELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1)
. Sé que lo que escribí podría lograrse con una simple consulta DONDE, pero solo la estaba usando para comprender EXISTS. Tengo todas las filas. ¿De hecho se debe al hecho de que no utilicé subconsultas correlacionadas? Gracias.EXISTS significa que la subconsulta devuelve al menos una fila, eso es realmente. En ese caso, es una subconsulta correlacionada porque verifica el proveedor_id de la tabla externa con el proveedor_id de la tabla interna. Esta consulta dice, en efecto:
SELECCIONAR todos los proveedores Para cada ID de proveedor, vea si existe un pedido para este proveedor Si el proveedor no está presente en la tabla de pedidos, elimine al proveedor de los resultados VOLVER a todos los proveedores que tienen filas correspondientes en la tabla de pedidos
Puede hacer lo mismo en este caso con INNER JOIN.
SELECT suppliers.* FROM suppliers INNER JOIN orders ON suppliers.supplier_id = orders.supplier_id;
El comentario de Ponies es correcto. Debería hacer agrupaciones con esa combinación o seleccionar distintas según los datos que necesite.
fuente
Lo que describe es una consulta con una subconsulta correlacionada .
(En general) es algo que debe intentar evitar escribiendo la consulta usando una combinación en su lugar:
SELECT suppliers.* FROM suppliers JOIN orders USING supplier_id GROUP BY suppliers.supplier_id
Porque de lo contrario, la subconsulta se ejecutará para cada fila de la consulta externa.
fuente
orders
que coincide con la condición de unión.