Consultas diferentes, o mesmo plano de execução

E ai pessoas!

Neste final de semana depois de ler alguns artigos perdidos pela internet, como o sobre “SQL SERVER – Convert IN to EXISTS – Performance Talk” e “SQL SERVER – Subquery or Join – Various Options – SQL Server Engine knows the Best” do Pinal Dave, fiquei com vontade de criar um plano de execução diferente entre IN e EXISTS, mas realmente é impossível (pelos meios normais)…

Com sono, até vi um plano de execução diferente entre os dois, mas era o frio e o sono me fazendo escrever consultas erradas… #FAIL

Para demonstrar isso segue alguns exemplos de consultas diferentes que retornam o mesmo resultado e possuem o mesmo plano de execução (que surgiram no blog do Pinal Dave e algumas adaptações):

-- use of = any
SELECT *
FROM HumanResources.Employee E
WHERE E.EmployeeID = ANY (SELECT EA.EmployeeID
FROM HumanResources.EmployeeAddress EA)
GO
-- use of = some
SELECT *
FROM HumanResources.Employee E
WHERE E.EmployeeID = SOME (SELECT EA.EmployeeID
FROM HumanResources.EmployeeAddress EA)
GO
-- use of in
SELECT *
FROM HumanResources.Employee E
WHERE E.EmployeeID IN (SELECT EA.EmployeeID
FROM HumanResources.EmployeeAddress EA)
GO
-- use of exists
SELECT *
FROM HumanResources.Employee E
WHERE EXISTS (SELECT *
FROM HumanResources.EmployeeAddress EA
WHERE EA.EmployeeID = E.EmployeeID)
GO
-- use of cross apply
SELECT *
FROM HumanResources.Employee E
CROSS APPLY (SELECT *
FROM HumanResources.EmployeeAddress EA
WHERE EA.EmployeeID = E.EmployeeID) EA
GO
-- use of join
SELECT *
FROM HumanResources.Employee E
INNER JOIN HumanResources.EmployeeAddress EA
ON E.EmployeeID = EA.EmployeeID
GO
-- use of join with subquery
SELECT *
FROM HumanResources.Employee E
INNER JOIN (SELECT *
FROM HumanResources.EmployeeAddress EA) EA
ON EA.EmployeeID = E.EmployeeID
GO

E o plano de execução:

Lembrando que há uma pequena diferença entre os quatro primeiros planos de execução e os três últimos, visto que o algoritmo de MERGE JOIN executado pelo primeiro grupo é um “LEFT SEMI JOIN” e o do segundo grupo é um “INNER JOIN”, assim o segundo grupo somente é valido para relacionamentos 1:0..1 (a não ser que você deseje que apareça dados da tabela “filha” no resultado, podendo fazer os dados da tabela “mãe” se repetirem para cada resultado encontrado na “filha”) e o primeiro grupo é valido tanto para relacionamentos 1:0..1 e 1:0..N.

As diferenças aparecem quando mudamos um pouco as consultas:

-- segundo grupo (INNER JOIN)
SELECT COUNT(*)
FROM HumanResources.Employee E
INNER JOIN HumanResources.EmployeeAddress EA
ON E.EmployeeID = EA.EmployeeID
GO
-- primero grupo (LEFT SEMI JOIN)
SELECT COUNT(*)
FROM HumanResources.Employee E
WHERE E.EmployeeID IN (SELECT EA.EmployeeID
FROM HumanResources.EmployeeAddress EA)
GO

E o plano de execução fica bem diferente, visto o INNER JOIN será semelhante a um COUNT(*) direto na tabela “filha” por causa de influência da foreign key:

Ou quando utilizamos NOT EXISTS/IN ou LEFT JOIN, podendo criar um plano de execução um pouco diferente, mas com o mesmo custo de execução:

-- segundo grupo (LEFT OUTER JOIN + FILTER)
SELECT *
FROM HumanResources.Employee E
LEFT JOIN HumanResources.EmployeeAddress EA
ON E.EmployeeID = EA.EmployeeID
WHERE EA.EmployeeID IS NULL
GO
-- primero grupo (LEFT ANTI SEMI JOIN)
SELECT *
FROM HumanResources.Employee E
WHERE E.EmployeeID NOT IN (SELECT EA.EmployeeID
FROM HumanResources.EmployeeAddress EA)
GO

Espero que tenham gostado, por enquanto eu fico por aqui (neste frio de 3ºC de Curitiba-PR), até mais!!!

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s