Sempre que preciso verificar a existência de alguma linha em uma tabela, costumo escrever sempre uma condição como:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Algumas outras pessoas escrevem como:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Quando a condição é em NOT EXISTS
vez de EXISTS
: Em algumas ocasiões, eu posso escrevê-la com uma LEFT JOIN
e uma condição extra (às vezes chamada de antijoin ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
Eu tento evitá-lo porque acho que o significado é menos claro, especialmente quando o que é seu primary_key
não é tão óbvio, ou quando sua chave primária ou sua condição de junção é composta por várias colunas (e você pode facilmente esquecer uma das colunas). No entanto, às vezes você mantém o código escrito por outra pessoa ... e ele está lá.
Existe alguma diferença (além do estilo) a ser usada em
SELECT 1
vez deSELECT *
?
Existe algum caso de esquina em que não se comporte da mesma maneira?Embora o que eu escrevi seja o padrão SQL (AFAIK): existe essa diferença para diferentes bancos de dados / versões mais antigas?
Existe alguma vantagem em escrever explicitamente um antijoin?
Os planejadores / otimizadores contemporâneos o tratam de maneira diferente daNOT EXISTS
cláusula?
fonte
EXISTS (SELECT FROM ...)
.Respostas:
Não, não há diferença na eficiência entre
(NOT) EXISTS (SELECT 1 ...)
e(NOT) EXISTS (SELECT * ...)
em todos os principais DBMS. Eu já vi muitas vezes(NOT) EXISTS (SELECT NULL ...)
sendo usado também.Em alguns, você pode até escrever
(NOT) EXISTS (SELECT 1/0 ...)
e o resultado é o mesmo - sem nenhum erro (divisão por zero), o que prova que a expressão não é avaliada.Sobre o
LEFT JOIN / IS NULL
método antijoin, uma correção: isso é equivalente aNOT EXISTS (SELECT ...)
.Nesse caso,
NOT EXISTS
vsLEFT JOIN / IS NULL
, você pode obter diferentes planos de execução. No MySQL, por exemplo, e principalmente nas versões mais antigas (anteriores à 5.7), os planos seriam bastante semelhantes, mas não idênticos. Os otimizadores de outros DBMS (SQL Server, Oracle, Postgres, DB2) são - até onde eu sei - mais ou menos capazes de reescrever esses 2 métodos e considerar os mesmos planos para ambos. Ainda assim, não existe essa garantia e, ao fazer a otimização, é bom verificar os planos de diferentes reescritas equivalentes, pois pode haver casos em que cada otimizador não reescreve (por exemplo, consultas complexas, com muitas junções e / ou tabelas derivadas / subconsultas dentro da subconsulta, onde as condições de várias tabelas, colunas compostas usadas nas condições de junção) ou as opções e planos do otimizador são afetados de maneira diferente pelos índices, configurações disponíveis etc.Observe também que
USING
não pode ser usado em todos os DBMS (SQL Server, por exemplo). Os trabalhos mais comuns emJOIN ... ON
todos os lugares.E as colunas precisam ser prefixadas com o nome da tabela / alias no
SELECT
para evitar erros / ambiguidades quando tivermos junções.Também geralmente prefiro colocar a coluna unida na
IS NULL
verificação (embora a PK ou qualquer coluna não anulável esteja OK, pode ser útil para a eficiência quando o planoLEFT JOIN
usa um índice não agrupado):Há também um terceiro método para antijogo, usando,
NOT IN
mas ele tem semântica diferente (e resultados!) Se a coluna da tabela interna for anulável. No entanto, pode ser usado excluindo as linhas comNULL
, tornando a consulta equivalente às 2 versões anteriores:Isso também geralmente gera planos semelhantes na maioria dos DBMS.
fonte
[NOT] IN (SELECT ...)
, embora equivalentes, tiveram um desempenho muito ruim. Evite isso!SELECT *
certamente está fazendo mais trabalho. Eu faria para aconselhar simplicidade bem usandoSELECT 1
Há uma categoria de casos em que
SELECT 1
eSELECT *
não são intercambiáveis - mais especificamente, um sempre será aceito nesses casos, enquanto o outro na maioria das vezes não.Estou falando de casos em que você precisa verificar a existência de linhas de um conjunto agrupado . Se a tabela
T
tiver colunasC1
eC2
você estiver verificando a existência de grupos de linhas que correspondem a uma condição específica, você pode usar oSELECT 1
seguinte:mas você não pode usar
SELECT *
da mesma maneira.Esse é apenas um aspecto sintático. Onde as duas opções são aceitas sintaticamente, você provavelmente não terá nenhuma diferença em termos de desempenho ou resultados retornados, como foi explicado na outra resposta .
Notas adicionais após comentários
Parece que muitos produtos de banco de dados não suportam essa distinção. Produtos como SQL Server, Oracle, MySQL e SQLite aceitarão com prazer
SELECT *
a consulta acima sem erros, o que provavelmente significa que eles tratam EXISTSSELECT
de uma maneira especial.O PostgreSQL é um RDBMS onde
SELECT *
pode falhar, mas ainda pode funcionar em alguns casos. Em particular, se você estiver agrupando pelo PK,SELECT *
funcionará bem, caso contrário, falhará com a mensagem:fonte
GROUP BY
, o conceito de*
não tem sentido (ou, pelo menos, não é tão claro).Uma maneira discutivelmente interessante de reescrever a
EXISTS
cláusula que resulta em uma consulta mais limpa e talvez menos enganosa, pelo menos no SQL Server, seria:A versão anti-semi-join seria semelhante a:
Ambos são normalmente otimizados para o mesmo plano que
WHERE EXISTS
ouWHERE NOT EXISTS
, mas a intenção é inconfundível e você não tem um "estranho"1
ou*
.Curiosamente, os problemas de verificação nula associados a
NOT IN (...)
são problemáticos<> ALL (...)
, enquanto oNOT EXISTS (...)
não sofre com esse problema. Considere as duas tabelas a seguir com uma coluna anulável:Adicionaremos alguns dados a ambos, com algumas linhas correspondentes e outras que não:
A
NOT IN (...)
consulta:Tem o seguinte plano:
A consulta não retorna linhas, pois os valores NULL tornam impossível a igualdade de confirmação.
Esta consulta, com
<> ALL (...)
mostra o mesmo plano e não retorna linhas:A variante utilizada
NOT EXISTS (...)
mostra uma forma de plano ligeiramente diferente e retorna linhas:O plano:
Os resultados dessa consulta:
Isso torna o uso
<> ALL (...)
tão propenso a resultados problemáticos quantoNOT IN (...)
.fonte
*
estranho: leioEXISTS (SELECT * FROM t WHERE ...)
ASthere is a _row_ in table _t_ that...
. De qualquer forma, gosto de ter alternativas, e a sua é claramente legível. Uma dúvida / ressalva: como se comportará seb
for anulável? [Eu tive más experiências e algumas noites curtas ao tentar descobrir uma misstake causada por umax IN (SELECT something_nullable FROM a_table)
]A "prova" de que eles são idênticos (no MySQL) é fazer
depois repita com
SELECT 1
. Nos dois casos, a saída 'estendida' mostra que foi transformada emSELECT 1
.Da mesma forma,
COUNT(*)
é transformado emCOUNT(0)
.Outra coisa a ser observada: melhorias na otimização foram feitas nas versões recentes. Pode valer a pena comparar
EXISTS
vs anti-junções. Sua versão pode fazer um trabalho melhor com uma versus a outra.fonte
Em alguns bancos de dados, essa otimização ainda não funciona. Como por exemplo no PostgreSQL A partir da versão 9.6, isso irá falhar.
E isso terá sucesso.
Está falhando porque o seguinte falha, mas isso ainda significa que há uma diferença.
Você pode encontrar mais informações sobre essa peculiaridade em particular e a violação das especificações na minha resposta à pergunta: A Especificação SQL requer um GROUP BY em EXISTS ()
fonte
Eu sempre usei
select top 1 'x'
(SQL Server)Teoricamente,
select top 1 'x'
seria mais eficiente queselect *
, como o primeiro estaria completo após selecionar uma constante na existência de uma linha de qualificação, enquanto o segundo selecionaria tudo.No entanto, embora muito cedo possa ter sido relevante, a otimização tornou a diferença irrelevante em provavelmente todos os principais RDBS.
fonte
top n
semorder by
é uma boa idéia.select top 1 'x'
não deve ser mais eficiente do queselect *
em umaExist
expressão. Na prática, pode ser mais eficiente se o otimizador estiver funcionando abaixo do ideal, mas teoricamente ambas as expressões são equivalentes.IF EXISTS(SELECT TOP(1) 1 FROM
é um hábito melhor a longo prazo e entre plataformas, simplesmente porque você nem precisa começar a se preocupar com o quão boa ou ruim é a sua plataforma / versão atual; e SQL está passando deTOP n
para parametrizávelTOP(n)
. Essa deve ser uma habilidade de aprender uma vez.fonte
TOP
nem é SQL válido.TOP (n)
no "SQL" - a linguagem de consulta padrão. Há um no T-SQL que é o dialeto que o Microsoft SQL Server está usando.