¿Debo usar SQL JOIN o IN Cláusula?

13

Tengo una pregunta sobre el mejor enfoque. No estoy seguro de qué enfoque es mejor cuando los datos se consideran de tamaño variable.

Considere las siguientes 3 TABLAS:

EMPLEADO

EMPLOYEE_ID, EMP_NAME

PROYECTO

PROJECT_ID, PROJ_NAME

EMP_PROJ (muchas a muchas de las dos tablas anteriores)

EMPLOYEE_ID, PROJECT_ID

Problema : dado un Id. De empleado, encuentre TODOS los empleados de TODOS los proyectos con los que este empleado esté asociado.

He intentado esto de dos maneras ... ambos enfoques difieren solo en unos pocos milisegundos, sin importar el tamaño de los datos que se utilicen.

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)

Vamos

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 ahora, espero alrededor de 5000 empleados y proyectos cada uno ... pero no tengo idea de qué tipo de relación existe. ¿Qué enfoque recomendarías? ¡Gracias!

EDITAR: Plan de Ejecución del Enfoque 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)"

Plan de Ejecución del Enfoque 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 el plan de Ejecución del Enfoque 2 es ligeramente mejor, porque el "costo" es 60 en lugar del 85 del enfoque 1. ¿Es esa la forma correcta de analizar esto?

¿Cómo se sabe que será válido incluso para todo tipo de combinaciones de muchos?

rk2010
fuente
3
Parece que un plan de Postgres me explica. Personalmente, seguiría el enfoque basado en unirse, pero leí algunas de las respuestas a continuación sobre la reescritura de la consulta. Ah, y sugeriría que el OP use explicar analizar en lugar de solo explicar.
xzilla
Estoy de acuerdo con xzilla: explain analyzepodría revelar más diferencias entre los planes
a_horse_with_no_name

Respuestas:

14

En SQL Server, con algunas suposiciones como "esos campos no pueden contener NULL", esas consultas deberían dar casi el mismo plan.

Pero también considere el tipo de unión que está haciendo. Una cláusula IN como esta es una Semi Join, no una Inner Join. Una unión interna puede proyectarse en varias filas, dando duplicados (en comparación con el uso de IN o EXISTS). Por lo tanto, es posible que desee considerar este comportamiento al elegir cómo escribir su consulta.

Rob Farley
fuente
2
Estoy de acuerdo con el uso de existe en lugar de una combinación cuando intento evitar la duplicación. Desde mi propia experiencia con el servidor SQL, existía y la unión interna producía el mismo plan de consulta de todos modos. Tenía algunas inquietudes de rendimiento sobre las declaraciones 'in' pero solo aparecieron cuando la selección en la declaración in comenzó a devolver varios miles de filas.
GrumpyMonkey
66
@GrumpyMonkey: en SQL Server 2005+ INy EXISTSsiempre doy el mismo plan en mi experiencia. NOT INy NOT EXISTSson diferentes sin embargo con NOT EXISTSpreferencia - Algunas comparaciones de rendimiento aquí
Martin Smith
8

Lo que busca su consulta es simplemente

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

o

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
fuente
¿No sería más rápida la subconsulta si fuera en SELECT 1lugar de SELECT *?
Daniel Serodio
Puede depender de DBMS. Sé con certeza que SQL-Server está optimizando Select *. (cf. Itzik Ben-Gan en Microsoft® SQL Server® 2012 T-SQL Fundamentals)
bernd_k
0

Puedes probar 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
fuente