SQL: busque registros de una tabla que no existan en otra


310

Tengo las siguientes dos tablas SQL (en MySQL):

Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1  | John | 111111111111 |
+----+------+--------------+
| 2  | Jane | 222222222222 |
+----+------+--------------+

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1  | 0945 | 111111111111 |
+----+------+--------------+
| 2  | 0950 | 222222222222 |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

¿Cómo puedo averiguar qué llamadas fueron realizadas por personas que phone_numberno están en el Phone_book? El resultado deseado sería:

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

Cualquier ayuda sería muy apreciada.

Respuestas:


439

Hay varias formas diferentes de hacerlo, con eficiencia variable, dependiendo de qué tan bueno sea su optimizador de consultas y el tamaño relativo de sus dos tablas:

Esta es la declaración más corta, y puede ser más rápida si su directorio telefónico es muy corto:

SELECT  *
FROM    Call
WHERE   phone_number NOT IN (SELECT phone_number FROM Phone_book)

alternativamente (gracias a Alterlife )

SELECT *
FROM   Call
WHERE  NOT EXISTS
  (SELECT *
   FROM   Phone_book
   WHERE  Phone_book.phone_number = Call.phone_number)

o (gracias a WOPR)

SELECT * 
FROM   Call
LEFT OUTER JOIN Phone_Book
  ON (Call.phone_number = Phone_book.phone_number)
  WHERE Phone_book.phone_number IS NULL

(ignorando que, como han dicho otros, normalmente es mejor seleccionar solo las columnas que desea, no ' *')


1
evite IN, use EXISTS - la pista está en el título de la pregunta
annakata

28
La combinación externa izquierda es probablemente la más rápida en el caso general, ya que evita la ejecución repetida de la subconsulta.
WOPR

No para ser exigente, pero la subconsulta en mi sugerencia devuelve <code> select 'x' </code> y no <code> select * </code>
Alterlife

sí - el manual de MySQL sugiere que esto es normal para una consulta 'EXISTE'
Alnitak

2
@Alnitak: en la segunda consulta no es necesario SELECT *en la subconsulta. En cambio, por ejemplo SELECT 1, debería ser lo suficientemente bonito.
Alexander Abakumov

90
SELECT Call.ID, Call.date, Call.phone_number 
FROM Call 
LEFT OUTER JOIN Phone_Book 
  ON (Call.phone_number=Phone_book.phone_number) 
  WHERE Phone_book.phone_number IS NULL

Debería eliminar la subconsulta, permitiendo que el optimizador de consultas haga su magia.

Además, evite "SELECCIONAR *" porque puede romper su código si alguien altera las tablas o vistas subyacentes (y es ineficiente).


10
Este es generalmente el método más eficiente ya que no realiza múltiples pases en la segunda tabla ... espero que algunas personas estén leyendo los comentarios.
Nerdfest

3
Prefiero esperar que la gente haga un perfil: a menos que seas un gurú superior del rendimiento de SQL, decir con anticipación cuál será el más rápido es bastante difícil (y depende del motor DBMS que uses).
bortzmeyer

2
La notación Big O le dirá fácilmente qué puede esperar que sea más rápido en este caso. Son órdenes de magnitud diferentes.
Jonesopolis

Vea la respuesta de Afterlife y mi comentario allí, si hay una 1:Nrelación entre sus dos tablas. O agregue DISTINCTcomo se ve en la respuesta de Vlado
ToolmakerSteve

25

El siguiente código sería un poco más eficiente que las respuestas presentadas anteriormente cuando se trata de conjuntos de datos más grandes.

SELECT * FROM Call WHERE 
NOT EXISTS (SELECT 'x' FROM Phone_book where 
Phone_book.phone_number = Call.phone_number)

1
Como siempre, vale la pena perfilar el rendimiento de las consultas contra su conjunto de datos de destino para elegir el que tenga el mejor rendimiento. Los optimizadores de SQL son lo suficientemente buenos en estos días que los resultados de rendimiento a menudo son sorprendentes.
Greg Hewgill

1
Una ventaja de este enfoque (frente a LEFT OUTER JOIN by WOPR) es que evita la devolución de varias filas por fila de Call, si hay varias filas coincidentes Phone_book. Es decir, si hay una 1:Nrelación entre sus dos tablas.
ToolmakerSteve

Comenzaría con este: representa directamente la intención. Si el rendimiento no es lo suficientemente bueno, asegúrese de que existan índices adecuados. Solo entonces, pruebe lo menos obvio LEFT OUTER JOIN, vea si su rendimiento es mejor.
ToolmakerSteve

6
SELECT DISTINCT Call.id 
FROM Call 
LEFT OUTER JOIN Phone_book USING (id) 
WHERE Phone_book.id IS NULL

Esto devolverá los id-s adicionales que faltan en su tabla Phone_book.


4

Yo creo que

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL

La idcolumna de la calltabla no tiene el mismo valor que la idcolumna de la Phone_booktabla, por lo que no puede unirse a estos valores. Vea la respuesta de WOPR para un enfoque similar.
Michael Fredrickson

3
SELECT t1.ColumnID,
CASE 
    WHEN NOT EXISTS( SELECT t2.FieldText  
                     FROM Table t2 
                     WHERE t2.ColumnID = t1.ColumnID) 
    THEN t1.FieldText
    ELSE t2.FieldText
END FieldText       
FROM Table1 t1, Table2 t2

Esto le devolverá los datos de una tabla si los datos no están presentes en otra tabla para la misma columna
Harvinder Sidhu

1
SELECT name, phone_number FROM Call a
WHERE a.phone_number NOT IN (SELECT b.phone_number FROM Phone_book b)

Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar una aclaración de un autor, deje un comentario debajo de su publicación. - De la opinión
Dennis Kriechel

@DennisKriechel actualizó la consulta para que sea más específica a la pregunta.
JoshYates1980

1

Alternativamente,

select id from call
minus
select id from phone_number

1
No estoy seguro de que esto responda la pregunta como es (aunque el operador MENOS) es una nueva adición. Esto terminó en la cola de baja calidad; es posible que desee mejorar esta respuesta.
ste-fu
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.