2013-05-01 5 views
4

У меня есть отдельные группы «классы» и «группы», каждому из которых присваивается один или несколько тегов. Я хотел бы найти для каждой группы подмножество классов, которое содержит те же (или более) теги для каждой группы.SQL найти наборы с общими членами (реляционное деление)

Некоторые образцы данных:

declare @Groups table 
(
    GroupID int, 
    TagID int 
) 

insert @Groups 
values (1,1),(1,2),(1,3), 
    (2,1),(2,2), 
    (3,1),(3,2),(3,3),(3,4) 

declare @Classes table 
(
    ClassID int, 
    TagID int 
) 

insert @Classes 
values (1,1),(1,2), 
    (2,1),(2,2), 
    (3,1),(3,2),(3,3) 

select * from @Groups 
select * from @Classes 

И выход:

GroupID TagID 
1  1 
1  2 
1  3 
2  1 
2  2 
3  1 
3  2 
3  3 
3  4 

ClassID TagID 
1  1 
1  2 
2  1 
2  2 
3  1 
3  2 
3  3 

Пример результирующего набора будет выглядеть следующим образом:

declare @Results table 
(
    GroupID int, 
    ClassID int 
) 

insert @Results 
values (1,3),(2,1),(2,2),(2,3),(3,null) 

select * from @Results 

Результаты Выход:

GroupID ClassID 
1  3 
2  1 
2  2 
2  3 
3  NULL 

Я понимаю, что это проблема типа реляционного деления, включающая having и count. Эти сообщения описывают то, что я хочу сделать, но я не могу понять, как применить примеры конкретного случая выше:

ответ

4

Я думаю, что это должно работать

select distinct g.GroupID, c.ClassID 
from @Groups g 
    left join @Classes c on g.TagID = c.TagID 
where not exists (
    select * 
    from @Groups g2 
    where g2.GroupID = g.GroupID 
     and g2.TagID not in (
      select TagID 
      from @Classes c2 
      where c2.ClassID = c.ClassID 
     ) 
    ) or c.ClassID is null 
+0

Добро пожаловать в SO! и +1 для получения метода двойного отрицания. Я попытался бы избежать этого в производственных средах, потому что это может быть довольно озадачивающим, если вы не знакомы с ним. – Andomar

+0

Спасибо, помечены как ответ для корректного возврата null для пустых групп. – Tamar

2

Вы можете присоединиться к столам вместе и требую, чтобы все теги из группы найдены в классе:

select g.GroupID 
,  c.ClassID 
from @Groups g 
join @Classes c 
on  c.TagID = g.TagID 
group by 
     g.GroupID 
,  c.ClassID 
having count(c.TagID) = 
     (
     select count(*) 
     from @Groups g2 
     where g2.GroupID = g.GroupID 
     ) 

Это не список групп без класса соответствия, и я не могу думать о простом способе сделать это.

Example at SQL Fiddle.

+0

Спасибо! Похоже, что это делает работу, подальше от тестовой системы прямо сейчас, но подтвердит ее после полной проверки. – Tamar

+0

"требуют, чтобы все теги из группы были найдены в классе" ... Спасибо за ясную логику тоже, подзапрос был недостающим звеном для меня. – Tamar