¿Cómo seleccionar filas sin entrada coincidente en otra tabla?


323

Estoy haciendo un trabajo de mantenimiento en una aplicación de base de datos y descubrí que, alegría de alegrías, a pesar de que los valores de una tabla se utilizan al estilo de las claves externas, no hay restricciones de clave externa en las tablas.

Estoy tratando de agregar restricciones FK en estas columnas, pero estoy descubriendo que, debido a que ya hay una gran cantidad de datos incorrectos en las tablas de errores anteriores que se han corregido ingenuamente, necesito encontrar las filas que no coincidir con la otra tabla y luego eliminarlos.

Encontré algunos ejemplos de este tipo de consulta en la web, pero todos parecen proporcionar ejemplos en lugar de explicaciones, y no entiendo por qué funcionan.

¿Alguien puede explicarme cómo construir una consulta que devuelva todas las filas sin coincidencias en otra tabla, y qué está haciendo, para que pueda hacer estas consultas yo mismo, en lugar de venir a SO para cada tabla en este desastre que tiene sin restricciones FK?

Respuestas:


614

Aquí hay una consulta simple:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

Los puntos clave son:

  1. LEFT JOINse usa esto devolverá TODAS las filas Table1, independientemente de si hay una fila coincidente o no Table2.

  2. La WHERE t2.ID IS NULLcláusula; esto restringirá los resultados devueltos solo a aquellas filas donde la ID devuelta Table2es nula; en otras palabras, NO hay ningún registro Table2para esa ID en particular Table1. Table2.IDse devolverá como NULL para todos los registros desde Table1donde la ID no coincida Table2.


44
Falla si una ID es NULL
Michael

169
@Michael: si tener una NULLidentificación es válida en su esquema, es posible que tenga problemas más grandes, ¿no estaría de acuerdo? :)
rinogo

1
¿funcionará esto incluso si la tabla1 tiene más registros que la tabla2? si la tabla 1 tiene 100 registros y la tabla 2 tiene 200 registros (100 que coinciden / se unen y 100 que no coinciden / se unen) ¿obtendríamos los 200 registros devueltos?
Juan Velez

1
A menudo me gusta ajustar la combinación izquierda como una vista de subconsulta / en línea para asegurar que no haya interacción entre la cláusula WHERE y la UNIÓN IZQUIERDA.
Andrew Wolfe

1
@Jas Punto clave 1 de la respuesta, TODAS las filas de la primera tabla, incluso las que no coinciden t1.ID = t2.ID condición de unión izquierda. Si cambia la primera línea SELECT t1.ID, t2.IDy elimina la línea WHERE, tendrá una mejor idea de cómo funciona.
Peter Laboš

97

Usaría EXISTSexpresión, ya que es más potente, puede, es decir, elegir con mayor precisión las filas a las que desea unir, en caso de LEFT JOINque tenga que tomar todo lo que está en la tabla unida. Su eficiencia es probablemente la misma que en el caso de la LEFT JOINprueba nula.

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

Algo así de simple es manejado fácilmente por el optimizador de consultas para una mejor ejecución.
Andrew Wolfe

2
Sí, la principal ventaja de EXISTSes su variabilidad.
Ondrej Bozek

1
¡Simple, elegante y resolvió mi problema! ¡Buena esa!
MikeMighty

2
En realidad, reduje la velocidad de una consulta que tuve de 7 segundos a 200 ms ... (en comparación con WHERE t2.id IS NULL) Gracias.
Moti Korets

44
@MotiKorets que quieres decir aumentó la velocidad :)
Ondrej Bozek

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

La Tabla 1 tiene una columna a la que desea agregar la restricción de clave externa, pero los valores en el foreign_key_id_columnno coinciden con los idde la tabla 2.

  1. La selección inicial enumera los ids de la tabla1. Estas serán las filas que queremos eliminar.
  2. La NOT INcláusula en la instrucción where limita la consulta a solo filas donde el valor en foreign_key_id_columnno está en la lista de la tabla 2 ids.
  3. La SELECTdeclaración entre paréntesis obtendrá una lista de todos los idcorreos electrónicos que se encuentran en la tabla 2.

@ zb226: su enlace tiene que ver con los límites de la INcláusula con una lista de valores literales. No se aplica al uso de una INcláusula con el resultado de una subconsulta. Esa respuesta aceptada a esa pregunta en realidad resuelve el problema mediante el uso de una subconsulta. (Una gran lista de valores literales es problemática porque crea una gran expresión SQL. Una subconsulta funciona bien porque, incluso si la lista resultante es grande, la expresión SQL en sí es pequeña.)
Kannan Goundan

@ KananGoundan Tienes toda la razón. Retirando el comentario defectuoso.
zb226

8

¿Dónde T2está la tabla a la que está agregando la restricción?

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

Y borra los resultados.


4

Tengamos las siguientes 2 tablas (salario y empleado) ingrese la descripción de la imagen aquí

Ahora quiero esos registros de la tabla de empleados que no están en salario. Podemos hacer esto de 3 maneras:

  1. Usando la unión interna
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

ingrese la descripción de la imagen aquí

  1. Usando la unión externa izquierda
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

ingrese la descripción de la imagen aquí

  1. Usando la unión completa
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

ingrese la descripción de la imagen aquí


2

De una pregunta similar aquí MySQL Inner Join Query para obtener registros que no están presentes en otra tabla Tengo esto para trabajar

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltablees donde le faltan registros, bigtablees donde tiene todos los registros. La consulta enumera todos los registros que no existen en el smalltablearchivo bigtable. Puede reemplazar idpor cualquier otro criterio coincidente.


0

Puede optar por Vistas como se muestra a continuación:

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

y luego trabaje en la vista para seleccionar o actualizar:

select * from AuthorizedUserProjectView where projectid = 49

que produce el resultado como se muestra en la imagen a continuación, es decir, para la columna nula no coincidente se ha completado.

[Result of select on the view][1]

0

No sabía cuál está optimizado (en comparación con @AdaTheDev), pero este parece ser más rápido cuando lo uso (al menos para mí)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

Si desea obtener cualquier otro atributo específico, puede usar:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

Puedes hacer algo como esto

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

¿Cómo seleccionar filas sin entrada coincidente en ambas tablas?

    seleccione * de [dbo]. [EmppDetails] e
     unión derecha [Empleado]. [Género] d en e.Gid = d.Gid
    donde e.Gid es nulo

    Unión 
    seleccione * de [dbo]. [EmppDetails] e
     izquierda unirse [Empleado]. [Género] d en e.Gid = d.Gid
    donde d.Gid es nulo

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.