2009-06-28 95 views
13

首先 - 为模糊标题道歉,我找不到更好的。SQL:多对多表AND查询

我有以下结构(简化)表:

EmpID DeptID 

1  1 
1  2 
2  1 
3  2 
4  5 
5  2 

此表代表一个多到多的关系。

我有兴趣找到与特定的一组DeptID相关的所有EmpID,例如我想要所有与DeptID1,2和3相关的EmpID。请注意这是与AND关系,而不是OR关系。对我而言,除了1,2和3之外,EmpID可能与额外的DeptID有关,因为它是有效的答案。

我感兴趣的DeptID数量发生了变化(例如,我可能希望EmpID与DeptID 3和5相关,或者我希望EmpID与DepID 2,3,4,5,6,7相关)。

当我尝试解决此问题时,我发现自己要么为每个DepID创建一个JOIN,要么为每个DeptID创建一个子查询。这意味着我必须根据我测试的DeptID的数量生成一个新的查询。我显然希望有一个参数或一组参数的静态查询。

我正在通过SQL Server和MySQL(并行开发两个版本的代码)工作。

任何想法?

回答

14

我假设你想找到在指定的所有部门的而不是仅仅是在各部门,这是一个更容易查询ANY员工的员工。

SELECT EmpID 
FROM mytable t1 
JOIN mytable t2 ON t1.EmpID = t2.EmpID AND t2.DeptID = 2 
JOIN mytable t3 ON t2.EmpID = t3.EmpID AND t3.DeptID = 3 
WHERE DeptID = 1 

我要抢占必然建议,会来使用聚合:

SELECT EmpID 
FROM mytable 
WHERE DeptID IN (1,2,3) 
GROUP BY EmpID 
HAVING COUNT(1) = 3 

抵制这种诱惑。这是显着较慢。与此类似的情况出现在SQL Statement - “Join” Vs “Group By and Having”中,第二个版本在第二个版本中约为,比慢了二十倍。我建议你看看Database Development Mistakes Made by AppDevelopers

3

我会从像开始:

SELECT EmpID, COUNT(*) AS NumDepts 
FROM thetable 
WHERE DeptID IN (1, 2, 3) 
GROUP BY EmpId 
HAVING COUNT(*) == 3 
当然

,即3在最后一行将永远是你正在检查部门ID的序列的长度(所以(2,3,4,5,6,7)这将是6)。这是表达“与所有这些部门相关的员工”的一种自然方式。

编辑:我在另一个关于性能问题的答案中看到一个注释 - 我已经在SQLite和PostgreSQL中用适当的索引尝试了这种方法,并且它看起来表现良好并适当使用了所有索引;而在MySQL 5.0中,我不得不承认性能并不如人意。我怀疑(没有机会在数十亿引擎上进行基准测试;-)其他非常好的SQL引擎(如SQL Server 2008,Oracle,IBM DB2,新的开源Ingres ...)也会优化这个查询,而其他平庸的(不能在任何地方靠近MySQL的地方想到任何流行的)。

所以,毫无疑问,您最喜欢的答案将取决于您真正关心的引擎(这需要我回到当时,十多年前,当时我的职责包括管理维护应该提供组件的团队对超过六种不同的引擎进行性能良好的查询 - 讨论噩梦般的工作...! - )。