2009-06-28 5 views
13

Tout d'abord, je ne peux pas trouver de meilleure solution pour le titre flou.SQL: table Many-To-Many ET requête

J'ai table avec la structure (simplification) suivante:

EmpID DeptID 

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

Ce tableau représente un nombre à plusieurs.

Je suis intéressé à trouver tous les EmpID qui sont liés à un groupe spécifique de DeptIDs, par exemple je veux tous les EmpIDs qui sont liés aux DeptIDs 1, 2 et 3. S'il vous plaît noter que c'est une relation AND et non Relation OR. Pour mon cas, le EmpID peut être lié à des DeptID supplémentaires en plus de 1, 2 et 3 pour que ce soit une réponse valide. Le nombre de DeptIDs qui m'intéresse change (c.-à-d. Que je veux des EmpID qui sont liés aux DeptID 3 et 5, ou que je puisse vouloir des EmpIDs liés aux DepIDs 2, 3, 4, 5, 6, 7). Lorsque j'essaie d'aborder ce problème, je me trouve soit en créant un JOIN par DepID, ou une sous-requête par DeptID. Cela signifierait que je dois générer une nouvelle requête par nombre de DeptIDs que je suis en train de tester. Je préférerais évidemment avoir une requête statique avec un paramètre ou un ensemble de paramètres. Je travaille sur SQL Server et MySQL (développant parallèlement deux versions de mon code).

Des idées?

Répondre

14

Je suppose que vous voulez trouver les employés qui sont ALL des services spécifiés et non seulement les employés qui sont QUELQUE des départements, ce qui est une question beaucoup plus facile.

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 

Je vais préempter la suggestion inévitable qui va venir à utiliser l'agrégation:

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

Résister à cette tentation. C'est de manière significative plus lent. Un scénario similaire à celui-ci est apparu dans SQL Statement - “Join” Vs “Group By and Having” et la deuxième version était, dans cette seconde, environ vingt fois plus lente.

Je vous suggère également de regarder Database Development Mistakes Made by AppDevelopers.

3

Je pars quelque chose comme:

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

bien sûr, que 3 dans la dernière ligne serait toujours la longueur de la séquence de ids de service que vous vérifiez (donc pour (2,3,4,5,6,7) il serait 6). C'est une façon naturelle d'exprimer «les employés connectés à tous ces départements». Edit: Je vois une note dans une autre réponse sur les problèmes de performance - J'ai essayé cette approche dans SQLite et PostgreSQL, avec des indices appropriés, et là, il semble que ça fonctionne bien et avec l'utilisation appropriée de tous ces indices; et en MySQL 5.0, où je dois admettre que la performance était nulle part aussi bonne.

Je soupçonne (sans une occasion de comparer cela sur un zillion plus de moteurs ;-) que d'autres très bons moteurs SQL (tels que SQL Server 2008, Oracle, IBM DB2, le nouveau open source Ingres ...) optimisera également bien cette requête, tandis que d'autres médiocres (ne peuvent pas penser à une popularité proche de MySQL) ne le seront pas. Donc, votre réponse préférée dépendra sans doute des moteurs qui vous intéressent vraiment (cela me ramène à l'époque, il y a plus d'une décennie, où mes responsabilités comprenaient la gestion de l'équipe qui maintenait un composant qui était censé fournir des requêtes performantes sur plus d'une demi-douzaine de moteurs disparates - parlez d'emplois cauchemardesques ...! -).