Nebudu komentovat, zda pro to existuje vhodnější schéma (je to docela možné), ale pro schéma se sloupci name
a item
, měl by fungovat následující dotaz. (syntaxe 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;
Myšlenka je taková, že máme všechny nastavené klíče v k
, které pak spojíme s daty sady položek v sets
jednou pro každou položku v sadě, kterou hledáme, v tomto případě tři. Každé ze tří vnitřních spojení s aliasy tabulky i1
, i2
a i3
odfiltrujte všechny názvy sad, které neobsahují položku hledanou pomocí tohoto spojení. Nakonec máme levé spojení s sets
s aliasem tabulky ix
, která přináší všechny položky navíc v sadě, tedy každou položku, kterou jsme nehledali. ix.name
je NULL
v případě, že nejsou nalezeny žádné položky navíc, což je přesně to, co chceme, tedy WHERE
doložka. Dotaz vrátí řádek obsahující klíč set, pokud je sada nalezena, jinak žádné řádky.
Upravit: Myšlenka odpovědí collapsars se zdá být mnohem lepší než ta moje, takže zde je trochu kratší verze s vysvětlením.
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;
Myšlenka je taková, že poddotaz s1
vybere klíče všech sad, které obsahují jiné položky než ty, které hledáme. Když jsme tedy odešli, připojte se k sets
s s1
, s1.name
je NULL
když sada obsahuje pouze položky, které hledáme. Potom seskupíme podle klíče sady a odfiltrujeme všechny sady, které mají nesprávný počet položek. Zbývají nám pak pouze sady, které obsahují pouze položky, které hledáme a mají správnou délku. Protože sady mohou obsahovat položku pouze jednou, může existovat pouze jedna sada splňující tato kritéria, a to je ta, kterou hledáme.
Upravit: Právě mi došlo, jak to udělat bez vyloučení.
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;
První dílčí dotaz zjišťuje celkový počet položek v každé sadě a druhý zjišťuje počet odpovídajících položek v každé sadě. Když matches.count
je 3, sada obsahuje všechny položky, které hledáme, a pokud totals.count
je také 3, sada neobsahuje žádné další položky.