Devo usar a cláusula SQL JOIN ou IN?

13

Eu tenho uma pergunta sobre a melhor abordagem. Não tenho certeza de qual abordagem é melhor quando os dados são considerados variáveis ​​em tamanho.

Considere as 3 TABELAS a seguir:

EMPREGADO

EMPLOYEE_ID, EMP_NAME

PROJETO

PROJECT_ID, PROJ_NAME

EMP_PROJ (muitas para muitas das duas tabelas acima)

EMPLOYEE_ID, PROJECT_ID

Problema : Dado um CódigoDoEmpregado, localize TODOS os funcionários de TODOS os Projetos aos quais este Funcionário está associado.

Eu tentei isso de duas maneiras. Ambas as abordagens diferem apenas em alguns milissegundos, independentemente do tamanho dos dados usados.

SELECT EMP_NAME FROM EMPLOYEE
WHERE EMPLOYEE_ID IN (
    SELECT EMPLOYEE_ID FROM EMP_PROJ    
    WHERE PROJECT_ID IN (
        SELECT PROJECT_ID FROM EMP_PROJ p, EMPLOYEE e
        WHERE p.EMPLOYEE_ID = E.EMPLOYEE_ID 
        AND  E.EMPLOYEE_ID = 123)

ir

select c.EMP_NAME FROM
(SELECT PROJECT_ID FROM EMP_PROJ
WHERE EMPLOYEE_ID = 123) a
JOIN 
EMP_PROJ b
ON a.PROJECT_ID = b.PROJECT_ID
JOIN 
EMPLOYEE c
ON b.EMPLOYEE_ID = c.EMPLOYEE_ID

A partir de agora, espero cerca de 5000 funcionários e projetos cada .. mas não tenho idéia sobre o tipo de relação entre muitos e muitos. Qual abordagem você recomendaria? obrigado!

EDIT: Plano de Execução da Abordagem 1

"Hash Join  (cost=86.55..106.11 rows=200 width=98)"
"  Hash Cond: (employee.employee_id = emp_proj.employee_id)"
"  ->  Seq Scan on employee  (cost=0.00..16.10 rows=610 width=102)"
"  ->  Hash  (cost=85.07..85.07 rows=118 width=4)"
"        ->  HashAggregate  (cost=83.89..85.07 rows=118 width=4)"
"              ->  Hash Semi Join  (cost=45.27..83.60 rows=118 width=4)"
"                    Hash Cond: (emp_proj.project_id = p.project_id)"
"                    ->  Seq Scan on emp_proj  (cost=0.00..31.40 rows=2140 width=8)"
"                    ->  Hash  (cost=45.13..45.13 rows=11 width=4)"
"                          ->  Nested Loop  (cost=0.00..45.13 rows=11 width=4)"
"                                ->  Index Scan using employee_pkey on employee e  (cost=0.00..8.27 rows=1 width=4)"
"                                      Index Cond: (employee_id = 123)"
"                                ->  Seq Scan on emp_proj p  (cost=0.00..36.75 rows=11 width=8)"
"                                      Filter: (p.employee_id = 123)"

Plano de execução da abordagem 2:

"Nested Loop  (cost=60.61..112.29 rows=118 width=98)"
"  ->  Index Scan using employee_pkey on employee e  (cost=0.00..8.27 rows=1 width=4)"
"        Index Cond: (employee_id = 123)"
"  ->  Hash Join  (cost=60.61..102.84 rows=118 width=102)"
"        Hash Cond: (b.employee_id = c.employee_id)"
"        ->  Hash Join  (cost=36.89..77.49 rows=118 width=8)"
"              Hash Cond: (b.project_id = p.project_id)"
"              ->  Seq Scan on emp_proj b  (cost=0.00..31.40 rows=2140 width=8)"
"              ->  Hash  (cost=36.75..36.75 rows=11 width=8)"
"                    ->  Seq Scan on emp_proj p  (cost=0.00..36.75 rows=11 width=8)"
"                          Filter: (employee_id = 123)"
"        ->  Hash  (cost=16.10..16.10 rows=610 width=102)"
"              ->  Seq Scan on employee c  (cost=0.00..16.10 rows=610 width=102)"

Parece que o plano de execução da Abordagem 2 é um pouco melhor, porque o 'custo' é 60 em oposição a 85 da abordagem 1. É o caminho certo para analisar isso?

Como alguém sabe que isso se aplica mesmo para todo tipo de combinação de muitas e muitas?

rk2010
fonte
3
Parece que um Postgres explica um plano para mim. Pessoalmente, eu iria com a abordagem baseada em junção, mas leria algumas das respostas abaixo sobre como reescrever a consulta. Ah, e eu sugiro que o OP use o analyse analyse, em vez de apenas explicar.
Xzilla 29/10
Concordo com xzilla: explain analyzepode revelar mais diferenças entre os planos
a_horse_with_no_name

Respostas:

14

No SQL Server, com algumas suposições como "esses campos não podem conter NULLs", essas consultas devem fornecer quase o mesmo plano.

Mas considere também o tipo de associação que você está fazendo. Uma cláusula IN como essa é uma junção semi, não uma junção interna. Uma junção interna pode projetar em várias linhas, fornecendo duplicatas (comparadas ao uso de IN ou EXISTS). Portanto, convém considerar esse comportamento ao escolher como você escreve sua consulta.

Rob Farley
fonte
2
Concordo com o uso de existe e não de uma associação ao tentar evitar a duplicação. De minha própria experiência com o SQL Server, a existência e a junção interna produziram o mesmo plano de consulta de qualquer maneira. Eu tinha algumas preocupações de desempenho sobre as instruções 'in', mas elas só apareceram quando o select na instrução in começou a retornar vários milhares de linhas.
GrumpyMonkey 29/10
6
@GrumpyMonkey - No SQL Server 2005+ INe EXISTSsempre dou o mesmo plano na minha experiência. NOT INe NOT EXISTSsão diferentes no entanto com o NOT EXISTSpreferido - Algumas comparações de desempenho aqui
Martin Smith
8

O que sua consulta está procurando é apenas

SELECT EMP_NAME 
FROM EMPLOYEE e
WHERE E.EMPLOYEE_ID = 123
and exists (select * from EMP_PROJ  where  EMPLOYEE_ID = 123);

ou

SELECT EMP_NAME 
FROM EMPLOYEE e
WHERE E.EMPLOYEE_ID = 123
and exists (select * from EMP_PROJ ep where  ep.EMPLOYEE_ID = E.EMPLOYEE_ID );
bernd_k
fonte
A subconsulta não seria mais rápida se fosse em SELECT 1vez de SELECT *?
21412 Daniel Serodio
Pode depender do DBMS. Tenho certeza de que o SQL-Server está otimizando o Select *. (cf. Itzik Ben-Gan no Microsoft® SQL Server® 2012 T-SQL Fundamentals)
bernd_k
0

Você pode tentar esta consulta:


select distinct e2.employee_id, ep.project_id 
from employee e, employee e2, emp_proj ep
where
e.employee_id = 123
and e.employee_id = ep.employee_id
and e2.project_id = ep.project_id;
techExplorer
fonte