Чи варто використовувати пункт SQL JOIN або IN?


13

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

Розглянемо наступні 3 ТАБЛИЦІ:

ПРАЦІВНИК

EMPLOYEE_ID, EMP_NAME

ПРОЕКТ

PROJECT_ID, PROJ_NAME

EMP_PROJ (багато-багато з вищезгаданих двох таблиць)

EMPLOYEE_ID, PROJECT_ID

Проблема : з огляду на EmployeeeID, знайдіть ВСІ співробітники ВСІХ проектів, з якими пов'язаний цей Співробітник.

Я спробував це двома способами .. обидва підходи відрізняються лише кількома мілісекундами незалежно від того, який розмір даних використовується.

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 співробітників та проектів у кожному .., але не маю уявлення про те, які існують багато-багато стосунків. Який підхід ви б рекомендували? Спасибі!

EDIT: План виконання підходу 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. Це правильний спосіб аналізу цього?

Звідки можна знати, що це справедливо навіть для всіляких поєднань багатьох-багатьох?


3
Схоже, Постгрес пояснює мені план. Особисто я б пішов з підходом на основі приєднання, але прочитав деякі відповіді нижче про перезапис запиту. О, і я б запропонував, що використання ОП слід пояснити, а не просто пояснити.
xzilla

Я згоден з xzilla: explain analyzeможе виявити більше відмінностей між планами
a_horse_with_no_name

Відповіді:


14

У SQL Server, маючи декілька припущень на кшталт "ці поля не можуть містити NULL", ці запити повинні мати майже той самий план.

Але також врахуйте тип приєднання, який ви робите. Запропонований IN такий, як напівприєднання Semi, а не Inner Join. Внутрішнє з'єднання може проектуватися на кілька рядків, тим самим даючи дублікати (порівняно з використанням IN або EXISTS). Тому ви можете врахувати таку поведінку, вибираючи, як ви пишете запит.


2
Я погоджуюсь із використанням існування, а не приєднання, коли намагаються уникати дублювання. З мого власного досвіду роботи з SQL-сервером було існування та внутрішнє з'єднання в будь-якому разі створило той самий план запитів. У мене були певні занепокоєння щодо висловлювань "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 );

Чи не був би запит швидшим, якби він був SELECT 1замість нього SELECT *?
Даніель Серодіо

Може залежати від СУБД. Я точно знаю, що SQL-сервер оптимізує Select *. (пор. Іцік Бен-Ган в Microsoft® 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.