Nie będę komentował, czy istnieje lepiej dopasowany schemat do tego (jest to całkiem możliwe), ale dla schematu z kolumnami name i item , poniższe zapytanie powinno działać. (składnia mysql)
SELECT k.name
FROM (SELECT DISTINCT name FROM sets) AS k
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1)
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3)
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5)
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5))
WHERE ix.name IS NULL;
Chodzi o to, że mamy wszystkie ustawione klucze w k , które następnie łączymy z danymi pozycji zestawu w sets raz na każdy element zestawu w zestawie, którego szukamy, w tym przypadku trzy. Każde z trzech sprzężeń wewnętrznych z aliasami tabeli i1 , i2 i i3 odfiltrować wszystkie nazwy zestawów, które nie zawierają elementu szukanego za pomocą tego sprzężenia. Na koniec mamy połączenie lewe z sets z aliasem tabeli ix , który zawiera wszystkie dodatkowe przedmioty w zestawie, czyli każdy przedmiot, którego nie szukaliśmy. ix.name jest NULL w przypadku, gdy nie znaleziono żadnych dodatkowych elementów, co jest dokładnie tym, czego chcemy, stąd WHERE klauzula. Zapytanie zwraca wiersz zawierający klucz zestawu, jeśli zestaw zostanie znaleziony, w przeciwnym razie żadnych wierszy.
Edytuj: Pomysł za odpowiedzią collapsars wydaje się być znacznie lepszy niż mój, więc oto nieco krótsza wersja tego z wyjaśnieniem.
SELECT sets.name
FROM sets
LEFT JOIN (
SELECT DISTINCT name
FROM sets
WHERE item NOT IN (1, 3, 5)
) s1
ON (sets.name = s1.name)
WHERE s1.name IS NULL
GROUP BY sets.name
HAVING COUNT(sets.item) = 3;
Pomysł polega na tym, że podzapytanie s1 wybiera klucze wszystkich zestawów zawierających elementy inne niż te, których szukamy. Tak więc, kiedy wyszliśmy, dołączamy do sets z s1 , s1.name jest NULL gdy zestaw zawiera tylko przedmioty, których szukamy. Następnie grupujemy według klucza zestawu i odfiltrowujemy wszystkie zestawy zawierające niewłaściwą liczbę elementów. Pozostają nam wtedy tylko zestawy, które zawierają tylko szukane przez nas przedmioty i mają odpowiednią długość. Ponieważ zestawy mogą zawierać element tylko raz, może istnieć tylko jeden zestaw spełniający te kryteria i to jest ten, którego szukamy.
Edytuj: Właśnie dotarło do mnie, jak to zrobić bez wykluczenia.
SELECT totals.name
FROM (
SELECT name, COUNT(*) count
FROM sets
GROUP BY name
) totals
INNER JOIN (
SELECT name, COUNT(*) count
FROM sets
WHERE item IN (1, 3, 5)
GROUP BY name
) matches
ON (totals.name = matches.name)
WHERE totals.count = 3 AND matches.count = 3;
Pierwsze podzapytanie znajduje całkowitą liczbę elementów w każdym zestawie, a drugie — liczbę pasujących elementów w każdym zestawie. Kiedy matches.count wynosi 3, zestaw zawiera wszystkie elementy, których szukamy, a jeśli totals.count ma również 3, zestaw nie zawiera żadnych dodatkowych elementów.