Выберите Записи, которые соответствуют ВСЕМ группам в таблице присоединения ко многим - PullRequest
0 голосов
/ 17 сентября 2009

У меня есть 2 таблицы: наборы и группы. Оба соединяются с использованием третьей таблицы set_has_groups. Я хотел бы получить наборы, которые имеют ВСЕ группы, которые я определяю

Один из способов сделать это будет

SELECT column1, column2 FROM sets WHERE 
id IN(SELECT set_id FROM set_has_group WHERE group_id = 1)
AND id IN(SELECT set_id FROM set_has_group WHERE group_id = 2)
AND id IN(SELECT set_id FROM set_has_group WHERE group_id = 3)

очевидно, что это не самое красивое решение

Я тоже пробовал это:

SELECT column1, column2 FROM sets WHERE 
id IN(SELECT set_id FROM set_has_group WHERE group_id IN(1,2,3) GROUP BY group_id
HAVING COUNT(*) = 3

Это выглядит красивее, но проблема в том, что выполнение занимает вечность. Первый запрос выполняется за 200 мс, а второй - более 1 минуты.

Есть идеи, почему это так?

=== UPDATE: Я играл с этим еще немного, и я изменил 2-й запрос, как это

SELECT columns FROM `set` WHERE id IN(
   select set_id FROM
      (
         SELECT set_id FROM set_has_group 
         WHERE group_id IN(1,2,3)
         GROUP BY set_id HAVING COUNT(*) = 3
      ) as temp         
)

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

Ответы [ 3 ]

1 голос
/ 17 сентября 2009

Я подозреваю небольшую опечатку во втором запросе.

Действительно, я не уверен. Вероятно, второй запрос выполняется посредством полного сканирования таблицы. В то же время первый «IN» действительно превращается в «EXISTS». Итак, вы можете попробовать использовать «существует». Например:

...
where 3 = (select count(*) from set_has_group 
    where group_id in (1, 2, 3) and set_id = id
    group by set_id)
0 голосов
/ 18 сентября 2009

Вот решение, которое использует некоррелированный подзапрос и не GROUP BY:

SELECT column1, column2 
FROM sets 
WHERE id IN (
  SELECT g1.set_id FROM set_has_group g1
  JOIN set_has_group g2 ON (g1.set_id = g3.set_id)
  JOIN set_has_group g3 ON (g1.set_id = g3.set_id)
  WHERE g1.group_id = 1 AND g2.group_id = 2 AND g3.group_id = 3);
0 голосов
/ 17 сентября 2009

Предполагая SQL Server, вот рабочий пример с JOIN, который должен работать лучше, чем предложения IN, которые вы используете, если у вас правильно установлены первичный и внешний ключи. Я построил объединенные 5 подходов в 3 группы, но наборы 4 и 5 не являются частью группы 3 и не будут отображаться в ответе. Однако этот запрос не масштабируется (например, поиск в группе 4, 5, 7, 8 и 13 потребует модификации кода, если вы не проанализируете входные параметры в табличной переменной)

set nocount on

declare @sets table
(
Id  INT Identity (1, 1),
Column1 VarChar (50),
Column2 VarChar (50)
)

declare @Set_Has_Group table
(
    Set_Id  Int,
    Group_Id    Int
)

insert into @sets values (newid(), newid())
insert into @sets values (newid(), newid())
insert into @sets values (newid(), newid())
insert into @sets values (newid(), newid())
insert into @sets values (newid(), newid())

update @sets set column1 = 'Column1 at Row ' + Convert (varchar, id)
update @sets set column2 = 'Column2 at Row ' + Convert (varchar, id)

insert into @Set_Has_Group values (1, 1)
insert into @Set_Has_Group values (1, 2)
insert into @Set_Has_Group values (1, 3)
insert into @Set_Has_Group values (2, 1)
insert into @Set_Has_Group values (2, 2)
insert into @Set_Has_Group values (2, 3)
insert into @Set_Has_Group values (3, 1)
insert into @Set_Has_Group values (3, 2)
insert into @Set_Has_Group values (3, 3)
insert into @Set_Has_Group values (4, 1)
insert into @Set_Has_Group values (4, 2)
insert into @Set_Has_Group values (5, 1)
insert into @Set_Has_Group values (5, 2)

/* your query with IN */
SELECT column1, column2 FROM @sets WHERE 
id IN(SELECT set_id FROM @set_has_group WHERE group_id = 1)
AND id IN(SELECT set_id FROM @set_has_group WHERE group_id = 2)
AND id IN(SELECT set_id FROM @set_has_group WHERE group_id = 3)

/* my query with JOIN */
SELECT * -- Column1, Column2
FROM    @sets sets
WHERE 3 = (
    SELECT Count (1)
    FROM @Set_Has_Group Set_Has_Group
    WHERE 1=1
        AND sets.Id = Set_Has_Group.Set_Id
        AND Set_Has_Group.Group_ID IN (1, 2, 3)
    Group by Set_Id
    )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...