У меня есть 2 таблицы: наборы и группы. Оба объединяются с помощью 3-й таблицы 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: Я играл с этим еще немного, и я изменил второй запрос, подобный этому
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
)
что очень быстро Это то же самое, что и второй запрос перед тем, как я его переношу в другую временную таблицу Довольно странно
Я подозреваю небольшое опечатки во втором запросе.
Действительно, я не уверен. Вероятно, второй запрос выполняется с помощью полного сканирования таблицы. В то же время первый "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)
Здесь используется решение, которое использует некоррелированный подзапрос и no 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);
Предполагая, что 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
)