SQL junta-se a subconsultas SQL (desempenho)?

110

Desejo saber se tenho uma consulta de junção parecida com esta -

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

e uma subconsulta parecida com esta -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

Quando considero o desempenho, qual das duas consultas seria mais rápida e por quê ?

Também há um momento em que devo preferir um ao outro?

Desculpe se isso é muito trivial e perguntado antes, mas estou confuso sobre isso. Além disso, seria ótimo se vocês pudessem me sugerir ferramentas que eu deveria usar para medir o desempenho de duas consultas. Muito obrigado!

Vishal
fonte
5
@Lucero, esta questão está marcada como sql-server-2008, onde o post que você mencionou está marcado como MySql. Você pode inferir que as respostas serão as mesmas. A otimização do desempenho é feita de maneira diferente nos dois RDBMSs.
Francois Botha

Respostas:

48

Eu ESPERO que a primeira consulta seja mais rápida, principalmente porque você tem uma equivalência e um JOIN explícito. Na minha experiência INé um operador muito lento, já que o SQL normalmente o avalia como uma série de WHEREcláusulas separadas por "OU" ( WHERE x=Y OR x=Z OR...).

Porém, como com ALL THINGS SQL, sua milhagem pode variar. A velocidade vai depender muito dos índices (você tem índices nas duas colunas do ID? Isso vai ajudar muito ...) entre outras coisas.

A única maneira REAL de saber com 100% de certeza o que é mais rápido é ativar o rastreamento de desempenho (IO Statistics é especialmente útil) e executar os dois. Certifique-se de limpar seu cache entre as execuções!

JNK
fonte
16
Tenho sérias dúvidas sobre essa resposta, uma vez que a maioria dos DBMS, definitivamente SQL Server 2008 e posterior, traduz a subconsulta de ID único (não correlacionado, o que significa: não faz referência a várias colunas de consulta externas) em uma semi-junção relativamente rápida. Além disso, conforme observado anteriormente em outra resposta, a primeira junção real retornará uma linha para CADA ocorrência do ID correspondente no Departamento - isso não faz diferença para um ID exclusivo, mas fornecerá toneladas de duplicatas em outro lugar. Classificar isso com DISTINCT ou GROUP BY será outra carga de desempenho pesado. Verifique os planos de execução no SQL Server Management Studio!
Erik Hart
2
A cláusula IN como equivalente a OR se aplica a listas de parâmetros / valores, mas não a subconsultas, que são geralmente tratadas como junções.
Erik Hart
42

Bem, eu acredito que é uma pergunta "Velha, mas dourada". A resposta é: "Depende!". As performances são um assunto tão delicado que seria bobagem dizer: "Nunca use subconsultas, sempre junte". Nos links a seguir, você encontrará algumas práticas recomendadas básicas que considero muito úteis:

Tenho uma tabela com 50000 elementos, o resultado que procurava era 739 elementos.

Minha pergunta no início foi esta:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

e demorou 7,9s para ser executado.

Minha pergunta finalmente é esta:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

e demorou 0.0256s

Bom SQL, bom.

linuxatico
fonte
3
Interessante, você poderia explicar como adicionar o GROUP BY corrigiu isso?
cozos
6
A tabela temporária gerada pela subconsulta era menor. Portanto, a execução é mais rápida, pois há menos dados para verificar.
Sirmyself
2
Eu acho que na primeira consulta você compartilhou a variável entre a consulta externa e a subconsulta, então para cada linha na consulta principal, a subconsulta é executada, mas na segunda a subconsulta é executada apenas uma vez e desta forma o desempenho melhorou.
Ali Faradjpour
1
Servidor Sql e MySql e ... Sql (exceto NoSql) são muito semelhantes em infraestrutura. Temos um tipo de mecanismo de otimização de consulta que converte as cláusulas IN (...) em join (se possível). Mas quando você tem um Grupo por em uma coluna bem indexada (com base em sua cardinalidade), então será muito mais rápido. Então realmente depende da situação.
Alix
10

Comece a examinar os planos de execução para ver as diferenças em como o SQl Server os interpretará. Você também pode usar o Profiler para realmente executar as consultas várias vezes e obter a diferença.

Eu não esperaria que eles fossem tão terrivelmente diferentes, onde você pode obter ganhos reais de grande desempenho usando joins em vez de subconsultas quando você usa subconsultas correlacionadas.

EXISTS é geralmente melhor do que qualquer um desses dois e quando você está falando de junções à esquerda onde deseja todos os registros que não estão na tabela de junção à esquerda, então NOT EXISTS é geralmente uma escolha muito melhor.

HLGEM
fonte
9

O desempenho é baseado na quantidade de dados que você está executando ...

Se for menos dados em torno de 20k. JOIN funciona melhor.

Se os dados forem mais de 100k +, então o IN funciona melhor.

Se você não precisa dos dados da outra tabela, IN é bom, mas é sempre melhor ir para EXISTS.

Todos esses critérios eu testei e as tabelas têm índices adequados.

JP Emvia
fonte
4

O desempenho deve ser o mesmo; é muito mais importante ter os índices e clusters corretos aplicados em suas tabelas (existem alguns bons recursos nesse tópico).

(Editado para refletir a pergunta atualizada)

Lucero
fonte
4

As duas consultas podem não ser semanticamente equivalentes. Se um funcionário trabalha para mais de um departamento (possível na empresa para a qual trabalho; isso implicaria que sua tabela não está totalmente normalizada), a primeira consulta retornaria linhas duplicadas, enquanto a segunda não. Para tornar as consultas equivalentes neste caso, a DISTINCTpalavra - chave teria que ser adicionada à SELECTcláusula, o que pode ter um impacto no desempenho.

Observe que há uma regra de design que estabelece que uma tabela deve modelar uma entidade / classe ou um relacionamento entre entidades / classes, mas não ambos. Portanto, sugiro que você crie uma terceira tabela, digamos OrgChart, para modelar o relacionamento entre funcionários e departamentos.

um dia quando
fonte
4

Sei que esse é um post antigo, mas acho que é um tópico muito importante, principalmente hoje em dia onde temos mais de 10 milhões de registros e falamos de terabytes de dados.

Também vou ponderar com as seguintes observações. Tenho cerca de 45 milhões de registros em minha tabela ([dados]) e cerca de 300 registros em minha tabela [gatos]. Tenho uma ampla indexação para todas as consultas sobre as quais estou prestes a falar.

Considere o Exemplo 1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

versus Exemplo 2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

O Exemplo 1 levou cerca de 23 minutos para ser executado. O exemplo 2 demorou cerca de 5 minutos.

Portanto, concluo que a subconsulta neste caso é muito mais rápida. Claro, lembre-se de que estou usando unidades SSD M.2 capazes de i / o @ 1 GB / s (isso é bytes, não bits), então meus índices são muito rápidos também. Portanto, isso pode afetar as velocidades também nas suas circunstâncias

Se for uma limpeza de dados única, provavelmente melhor deixá-la em execução e terminar. Eu uso TOP (10000) e vejo quanto tempo leva e multiplico pelo número de registros antes de atingir a grande consulta.

Se você estiver otimizando bancos de dados de produção, sugiro fortemente o pré-processamento de dados, ou seja, use gatilhos ou corretor de tarefas para atualizar registros assíncronos, de modo que o acesso em tempo real recupere dados estáticos.

Arvin Amir
fonte
0

Você pode usar um Plano Explicar para obter uma resposta objetiva.

Para o seu problema, um filtro Exists provavelmente teria o desempenho mais rápido.

Snekse
fonte
2
"um filtro Exists provavelmente teria o desempenho mais rápido" - provavelmente não, eu acho, embora uma resposta definitiva exija testes com os dados reais. Os filtros existentes tendem a ser mais rápidos onde há várias linhas com os mesmos valores de pesquisa - portanto, um filtro existente pode ser executado mais rápido se a consulta estiver verificando se outros funcionários foram registrados no mesmo departamento, mas provavelmente não ao comparar com um departamento tabela.
Ele funcionaria mais devagar nesse último cenário?
Snekse,
Dependeria do otimizador - em certas circunstâncias, poderia, mas normalmente eu esperaria um desempenho muito semelhante.