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

rk2*_*010 13 postgresql join

我有一个关于最佳方法的问题。当数据的大小可变时,我不确定哪种方法最好。

考虑以下 3 个表:

员工

EMPLOYEE_ID,EMP_NAME

项目

PROJECT_ID,PROJ_NAME

EMP_PROJ(以上两个表中的多对多)

EMPLOYEE_ID、PROJECT_ID

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

我以两种方式尝试过这个。无论使用什么大小的数据,这两种方法都只有几毫秒的不同。

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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

截至目前,我预计每个员工和项目大约有 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)"
Run Code Online (Sandbox Code Playgroud)

方法二的执行计划:

"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)"
Run Code Online (Sandbox Code Playgroud)

所以看起来方法 2 的执行计划稍微好一些,因为“成本”是 60,而不是方法 1 的 85。这是分析这个的正确方法吗?

人们怎么知道即使对于各种多对多组合也适用?

Rob*_*ley 14

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

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

  • @GrumpyMonkey - 根据我的经验,在 SQL Server 2005+ 中,`IN` 和 `EXISTS` 总是给出相同的计划。`NOT IN` 和 `NOT EXISTS` 是不同的,但是首选 `NOT EXISTS` - [这里有一些性能比较](http://sqlinthewild.co.za/index.php/2010/02/18/not-exists- vs-not-in/) (6认同)
  • 我同意在尝试避免重复时使用exists 而不是join。根据我自己对 SQL Server 的经验,存在和内部联接无论如何都会产生相同的查询计划。我确实对 'in' 语句有一些性能问题,但它们只在 in 语句中的 select 开始返回几千行时才浮出水面。 (2认同)

ber*_*d_k 8

您的查询正在寻找的只是

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

或者

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 );
Run Code Online (Sandbox Code Playgroud)