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.

Philip Morton
fuente

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 ' *')

Alnitak
fuente
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).

WOPR
fuente
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)
Alterlife
fuente
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.

Vlado
fuente
4

Yo creo que

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL
Nat Geo
fuente
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
Harvinder Sidhu
fuente
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)
JoshYates1980
fuente
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
elfekz
fuente
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