2009-06-28 2 views
13

Во-первых - извинения за нечеткое название, я не смог найти лучшего.SQL: запрос Many-To-Many и запрос

У меня есть таблица со следующей структурой (упрощение):

EmpID DeptID 

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

Эта таблица представляет многие-ко-многим.

Я заинтересован в поиске всех EmpID, связанных с определенной группой DeptID, например, я хочу, чтобы все EmpIDs были связаны с DeptIDs 1, 2 и 3. Обратите внимание, что это отношение AND, а не ИЛИ. Для моего случая, EmpID может быть связан с дополнительными DeptIDs, кроме 1, 2 и 3, чтобы он был действительным ответом.

Число DeptIDs Меня интересуют изменения (то есть, я могу захотеть EmpIDs, которые связаны с DeptID 3 и 5, или я могу захотеть EmpIDs, связанных с DepIDs 2, 3, 4, 5, 6, 7).

Когда я пытаюсь подойти к этой проблеме, я нахожусь либо в создании JOIN на DepID, либо в подзапросе на DeptID. Это означало бы, что я должен сгенерировать новый запрос на количество DeptID, с которыми я тестирую. Я, очевидно, предпочел бы иметь статический запрос с параметром или набором параметров.

Я работаю над SQL Server и MySQL (параллельно разрабатывая две версии моего кода).

Любые идеи?

ответ

14

Я предполагаю, что вы хотите найти сотрудников, которые находятся в ALL из указанных ведомств, а не только сотрудников, которые находятся в ЛЮБОГО отделов, что является гораздо проще запрос.

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 в последней строке всегда будет длиной последовательности отделов идентификаторов вы проверяете (так что для (2,3,4,5,6,7) было бы 6). Это один из естественных способов выразить «служащим, связанным со всеми этими отделами».

Редактировать: я вижу примечание в другом ответе о проблемах с производительностью. Я пробовал этот подход в SQLite и PostgreSQL с соответствующими индексами, и там, похоже, он работает хорошо и с надлежащим использованием всех указанных индексов; и в MySQL 5.0, где я должен признать, что производительность нигде не была такой хорошей.

Я подозреваю (без возможности сравнить это на более миллионах двигателей ;-), что другие действительно хорошие SQL-движки (такие как SQL Server 2008, Oracle, IBM DB2, новый с открытым исходным кодом Ingres ...) также будет хорошо оптимизировать этот запрос, в то время как другие посредственные (не могут думать о какой-либо популярности в любом месте рядом с MySQL) не будут.

Таким образом, несомненно, ваш любимый ответ будет зависеть от того, какие двигатели вы действительно заботитесь (это возвращает меня к тому времени, более десяти лет назад, когда мои обязанности включали управление командой, которая поддерживала компонент, который должен был предоставить хорошо выполненные запросы более чем на полдюжины разрозненных двигателей - рассказывают о работе кошмаров ...! -).

Смежные вопросы