我应该使用SQL JOIN还是IN子句?


13

我对最佳方法有疑问。当将数据视为大小可变时,我不确定哪种方法最好。

考虑以下3个表:

雇员

EMPLOYEE_ID,EMP_NAME

项目

PROJECT_ID,PROJ_NAME

EMP_PROJ(上面两个表中的许多表)

EMPLOYEE_ID,PROJECT_ID

问题:给定EmployeeID,找到与该Employee关联的所有项目的所有雇员。

我用两种方法尝试过。无论使用什么数据大小,这两种方法的区别仅相差几毫秒。

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,而不是方法1的85。这是分析此问题的正确方法吗?

怎么知道即使对于各种各样的许多很多组合,它也会成立?


3
看起来像Postgres向我解释计划。我个人会使用基于联接的方法,但是请阅读以下有关重写查询的答案。哦,我建议OP使用解释而不是仅仅解释。
xzilla 2011年

我同意xzilla:explain analyze可能会揭示计划之间的更多差异
a_horse_with_no_name

Answers:


14

在SQL Server中,有一些假设,例如“这些字段不能包含NULL”,这些查询应该给出几乎相同的计划。

但也要考虑您正在执行的联接类型。这样的IN子句是半联接,而不是内部联接。内部联接可以投影到多行上,从而提供重复项(与使用IN或EXISTS相比)。因此,在选择如何编写查询时,您可能需要考虑此行为。


2
我同意在尝试有效复制时使用存在而不是联接。从我自己对SQL Server的经验来看,存在和内部联接总会产生相同的查询计划。我确实对'in'语句有一些性能问题,但是只有在in语句中的select开始返回数千行时,它们才浮出水面。
GrumpyMonkey 2011年

6
@GrumpyMonkey-在SQL Server 2005+中INEXISTS根据我的经验,总是给出相同的计划。NOT INNOT 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 *
Daniel Serodio

可能取决于DBMS。我肯定知道SQL Server正在优化Select *。(请参阅Microsoft®SQLServer®2012 T-SQL基础知识中的
Itzik

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.