Должен ли я использовать SQL JOIN или IN предложение?

13

У меня есть вопрос о лучшем подходе. Я не уверен, какой подход лучше, когда данные считаются переменными по размеру.

Рассмотрим следующие 3 ТАБЛИЦЫ:

СОТРУДНИК

EMPLOYEE_ID, EMP_NAME

ПРОЕКТ

PROJECT_ID, PROJ_NAME

EMP_PROJ (много ко многим из двух таблиц выше)

EMPLOYEE_ID, PROJECT_ID

Проблема : По заданному идентификатору сотрудника найдите ВСЕХ сотрудников ВСЕХ проектов, с которыми связан данный Сотрудник.

Я попробовал это двумя способами ... оба подхода отличаются только на несколько миллисекунд, независимо от того, какой размер данных используется.

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)

идти

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

На данный момент я ожидаю около 5000 сотрудников и проектов каждый ... но понятия не имею, какие существуют отношения между многими и многими. Какой подход вы бы порекомендовали? Благодарность!

РЕДАКТИРОВАТЬ: План выполнения подхода 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)"

План выполнения подхода 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)"

Похоже, план выполнения подхода 2 немного лучше, потому что «стоимость» равна 60, а не 85 подхода 1. Это правильный способ проанализировать это?

Как можно знать, что это будет справедливо даже для всех видов множества комбинаций?

rk2010
источник
3
Похоже, Postgres объяснить мне план. Лично я бы пошел с подходом, основанным на объединении, но прочитал некоторые ответы ниже о переписывании запроса. О, и я бы предложил использовать ОП объяснить, а не просто объяснить.
xzilla
Я согласен с xzilla: explain analyzeможет выявить больше различий между планами
a_horse_with_no_name

Ответы:

14

В SQL Server с некоторыми допущениями, такими как «эти поля не могут содержать значения NULL», эти запросы должны давать почти тот же план.

Но также рассмотрите тип соединения, которое вы делаете. Предложение IN, подобное этому, является Semi Join, а не Inner Join. Внутреннее соединение может проецироваться на несколько строк, что дает дубликаты (по сравнению с использованием IN или EXISTS). Таким образом, вы можете рассмотреть это поведение при выборе способа написания запроса.

Роб Фарли
источник
2
Я согласен с использованием существует, а не объединение при попытке избежать дублирования. Исходя из моего собственного опыта работы с SQL-сервером, существовало и внутреннее объединение в любом случае создало один и тот же план запросов У меня были некоторые проблемы с производительностью в операторах in, но они появились только тогда, когда оператор select в операторе in начал возвращать несколько тысяч строк.
GrumpyMonkey
6
@GrumpyMonkey - В SQL Server 2005+ INи EXISTSвсегда дают один и тот же план по моему опыту. NOT INи NOT EXISTSотличаются, однако с NOT EXISTSпредпочтением - Некоторые сравнения производительности здесь
Мартин Смит
8

То, что ищет ваш запрос, просто

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

или

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
источник
Разве подзапрос не был бы быстрее, если бы он был SELECT 1вместо SELECT *?
Даниэль Серодио
Может зависеть от СУБД. Я точно знаю, что SQL-сервер оптимизирует Select *. (см. Ицик Бен-Ган в Microsoft T SQL Server 2012. Основы T-SQL)
bernd_k
0

Вы можете попробовать этот запрос:


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
источник