SQL - encontre registros de uma tabela que não existem em outra


310

Eu tenho as seguintes duas tabelas SQL (no 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 |
+----+------+--------------+

Como descobrir quais chamadas foram feitas por pessoas que phone_numbernão estão na Phone_book? A saída desejada seria:

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

Qualquer ajuda seria muito apreciada.

Respostas:


439

Há várias maneiras diferentes de fazer isso, com eficiência variável, dependendo da qualidade do otimizador de consultas e do tamanho relativo das duas tabelas:

Esta é a declaração mais curta e pode ser mais rápida se a sua agenda telefônica for muito curta:

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

alternativamente (graças a Alterlife )

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

ou (graças ao 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 já foi dito, normalmente é melhor selecionar apenas as colunas que você deseja, não ' *')


1
EM evitar, uso EXISTE - a dica é no título questão
annakata

28
A junção externa esquerda é provavelmente a mais rápida no caso geral, pois impede a execução repetida da subconsulta.
WOPR

Não é exigente, mas a subconsulta na minha sugestão retorna <code> selecione 'x' </code> e não <code> selecione * </code>
Alterlife

yes - O manual do MySQL sugere que isso é normal para uma consulta 'EXISTS'
Alnitak

2
@ Alnitak: Na segunda consulta, você não precisa SELECT *na subconsulta. Em vez disso, por exemplo SELECT 1, deve ser bastante 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

Deve remover a subconsulta, permitindo que o otimizador de consultas trabalhe sua mágica.

Além disso, evite "SELECT *", pois isso pode danificar seu código se alguém alterar as tabelas ou visualizações subjacentes (e é ineficiente).


10
Este é geralmente o método mais eficiente, pois não realiza várias passagens na segunda tabela ... espero que algumas pessoas estejam lendo os comunicados.
Nerdfest

3
Eu preferiria que as pessoas criassem perfil: a menos que você seja um dos principais gurus do desempenho do SQL, dizer com antecedência qual será o mais rápido é bastante difícil (e depende do mecanismo do DBMS usado).
Bortzmeyer

2
A notação Big O dirá facilmente o que você pode esperar ser o mais rápido nesse caso. São ordens de magnitude diferentes.
Jonesopolis 30/09/16

Veja a resposta do Afterlife e meu comentário, se houver um 1:Nrelacionamento entre suas duas tabelas. Ou adicione DISTINCTcomo visto na resposta de Vlado
ToolmakerSteve

25

O código abaixo seria um pouco mais eficiente do que as respostas apresentadas acima ao lidar com conjuntos de dados maiores.

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

1
Como sempre, vale a pena criar um perfil do desempenho das consultas no seu conjunto de dados de destino para escolher a que tem o melhor desempenho. Atualmente, os otimizadores de SQL são bons o suficiente para que os resultados de desempenho sejam surpreendentes.
Greg Hewgill

1
Uma vantagem dessa abordagem (vs. LEFT OUTER JOIN do WOPR) é que ela evita retornar várias linhas por linha Call, se houver várias linhas correspondentes Phone_book. Ou seja, se houver um 1:Nrelacionamento entre suas duas tabelas.
Home

Eu começaria com este - representa diretamente a intenção. Se o desempenho não for bom o suficiente, verifique se existem índices apropriados. Só então, tente o menos óbvio LEFT OUTER JOIN, veja se seu desempenho é melhor.
Home

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

Isso retornará os IDs extras ausentes na tabela Phone_book.


4

eu acho que

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

A idcoluna na calltabela não tem o mesmo valor que a idcoluna na Phone_booktabela, portanto você não pode ingressar nesses valores. Veja a resposta do WOPR para uma abordagem semelhante.
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

Isto irá voltar dados de uma tabela se os dados não está presente em outra mesa para a mesma coluna
Harvinder Sidhu

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

Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem. - Do comentário
Dennis Kriechel

A @DennisKriechel atualizou a consulta para que seja mais específica à pergunta.
precisa saber é o seguinte

1

Alternativamente,

select id from call
minus
select id from phone_number

1
Não tenho certeza se isso responde à pergunta como está (embora o operador MENOS) seja uma nova adição. Isso acabou na fila de baixa qualidade - você pode aprimorar esta resposta.
21317 st-fu
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.