Isenção de responsabilidade: descobri o problema (acho), mas queria adicionar esse problema ao Stack Overflow, pois não conseguia (facilmente) encontrá-lo em qualquer lugar. Além disso, alguém pode ter uma resposta melhor do que eu.
Eu tenho um banco de dados onde uma tabela "Comum" é referenciada por várias outras tabelas. Eu queria ver quais registros na tabela Common ficaram órfãos (ou seja, não tinham referências de nenhuma das outras tabelas).
Eu executei esta consulta:
select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)
Eu sei que existem registros órfãos, mas nenhum registro foi retornado. Por que não?
(Este é o SQL Server, se for o caso.)
sql
sql-server
tsql
Jeremy Stein
fonte
fonte
Respostas:
Atualizar:
Estes artigos no meu blog descrevem as diferenças entre os métodos em mais detalhes:
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:SQL Server
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:PostgreSQL
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:Oracle
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:MySQL
Existem três maneiras de fazer essa consulta:
LEFT JOIN / IS NULL
:NOT EXISTS
:NOT IN
:Quando
table1.common_id
não é anulável, todas essas consultas são semanticamente iguais.Quando é nulo,
NOT IN
é diferente, poisIN
(e, portanto,NOT IN
) retornaNULL
quando um valor não corresponde a nada em uma lista que contém aNULL
.Isso pode ser confuso, mas pode se tornar mais óbvio se lembrarmos da sintaxe alternativa para isso:
O resultado dessa condição é um produto booleano de todas as comparações na lista. Obviamente, um único
NULL
valor produz oNULL
resultado que renderiza o resultado inteiroNULL
também.Nunca podemos dizer definitivamente que isso
common_id
não é igual a nada dessa lista, pois pelo menos um dos valores éNULL
.Suponha que tenhamos esses dados:
LEFT JOIN / IS NULL
eNOT EXISTS
retornará3
,NOT IN
não retornará nada (pois sempre será avaliado como umFALSE
ouNULL
).In
MySQL
, no caso de coluna não anulável,LEFT JOIN / IS NULL
eNOT IN
é um pouco (vários por cento) mais eficiente queNOT EXISTS
. Se a coluna for anulável,NOT EXISTS
é a mais eficiente (novamente, não muito).Em
Oracle
, todas as três consultas geram os mesmos planos (umANTI JOIN
).Em
SQL Server
,NOT IN
/NOT EXISTS
são mais eficientes, poisLEFT JOIN / IS NULL
não podem ser otimizados para umANTI JOIN
por seu otimizador.Em
PostgreSQL
,LEFT JOIN / IS NULL
eNOT EXISTS
são mais eficientes do queNOT IN
, seno, eles são otimizados para anAnti Join
, whileNOT IN
useshashed subplan
(ou até simples,subplan
se a subconsulta for muito grande para o hash)fonte
NOT EXISTS
avalia como TRUE se a consulta dentro dela retornar alguma linha.SELECT NULL
como poderia serSELECT *
ouSELECT 1
ou qualquer outra coisa, oNOT EXISTS
predicado não olha para os valores das linhas, apenas os conta.Se você deseja que o mundo seja um local booleano de dois valores, você deve evitar o caso nulo (terceiro valor).
Não escreva cláusulas IN que permitam valores nulos no lado da lista. Filtre-os!
fonte
common_id not in
, ainda podemos ter umcommon_id
valor que éNULL
. Portanto, o problema de não obter resultados ainda persiste?Tabela1 ou Tabela2 possui alguns valores nulos para common_id. Use esta consulta:
fonte
fonte
Apenas fora do topo da minha cabeça...
Fiz alguns testes e aqui estavam meus resultados, a resposta de wrt @ patmortech e os comentários de @ rexem.
Se Tabela1 ou Tabela2 não estiver indexada no commonID, você fará uma varredura de tabela, mas a consulta do @ patmortech ainda será duas vezes mais rápida (para uma tabela mestre de 100K linhas).
Se nenhum deles estiver indexado no commonID, você realiza duas varreduras de tabela e a diferença é insignificante.
Se ambos estiverem indexados no commonID, a consulta "não existe" será executada em 1/3 do tempo.
fonte
fonte
Vamos supor esses valores para common_id:
Queremos que a linha no Common retorne, porque não existe em nenhuma das outras tabelas. No entanto, o nulo joga em uma chave de macaco.
Com esses valores, a consulta é equivalente a:
Isso é equivalente a:
É aqui que o problema começa. Ao comparar com um nulo, a resposta é desconhecida . Portanto, a consulta se reduz a
falso ou desconhecido é desconhecido:
verdadeiro e não desconhecido também é desconhecido:
A condição where não retorna registros onde o resultado é desconhecido, portanto, não recuperamos registros.
Uma maneira de lidar com isso é usar o operador existente em vez de entrar. Existe nunca retorna desconhecido, porque opera em linhas e não em colunas. (Uma linha existe ou não; nada dessa ambiguidade nula no nível da linha!)
fonte
isso funcionou para mim :)
fonte
NOT IN
performance lá?fonte
Eu tinha um exemplo em que estava olhando para cima e, como uma tabela continha o valor como um dobro, a outra como uma sequência, elas não coincidiam (ou não correspondiam sem uma conversão). Mas apenas NÃO IN . Como SELECT ... IN ... funcionou. Estranho, mas pensei em compartilhar caso mais alguém encontre essa solução simples.
fonte
Siga o exemplo abaixo para entender o tópico acima:
Além disso, você pode visitar o link a seguir para conhecer o Anti join
Mas se usarmos
NOT IN
nesse caso, não obteremos dados.Isso está acontecendo porque (
select department_id from hr.employees
) está retornando um valor nulo e a consulta inteira é avaliada como falsa. Podemos ver isso se alterarmos o SQL ligeiramente como abaixo e manipularmos valores nulos com a função NVL.Agora estamos obtendo dados:
Mais uma vez, estamos obtendo dados, pois lidamos com o valor nulo com a função NVL.
fonte