Ik zal niet ingaan op de vraag of er een beter geschikt schema is om dit te doen (het is heel goed mogelijk), maar voor een schema met kolommen name
en item
, zou de volgende query moeten werken. (mysql-syntaxis)
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;
Het idee is dat we alle ingestelde sleutels in k
. hebben , die we vervolgens samenvoegen met de ingestelde itemgegevens in sets
één keer voor elk setitem in de set waarnaar we zoeken, drie in dit geval. Elk van de drie inner joins met tabelaliassen i1
, i2
en i3
filter alle setnamen uit die niet het item bevatten waarnaar met die join wordt gezocht. Eindelijk hebben we een left join met sets
met tabelalias ix
, die alle extra items in de set bevat, dat wil zeggen, elk item waarnaar we niet op zoek waren. ix.name
is NULL
in het geval dat er geen extra items worden gevonden, wat precies is wat we willen, dus de WHERE
clausule. De query retourneert een rij met de set-sleutel als de set wordt gevonden, anders geen rijen.
Bewerken: Het idee achter het antwoord van collapsars lijkt veel beter dan het mijne, dus hier is een iets kortere versie daarvan met uitleg.
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;
Het idee hier is dat subquery s1
selecteert de sleutels van alle sets die andere items bevatten dan degene die we zoeken. Dus, toen we vertrokken, sluit je aan bij sets
met s1
, s1.name
is NULL
wanneer de set alleen items bevat waarnaar we op zoek zijn. We groeperen vervolgens op setsleutel en filteren alle sets met het verkeerde aantal items eruit. We houden dan alleen sets over die alleen items bevatten waarnaar we op zoek zijn en die de juiste lengte hebben. Aangezien sets een item maar één keer kunnen bevatten, kan er maar één set zijn die aan die criteria voldoet, en dat is degene die we zoeken.
Bewerken: Het drong tot me door hoe ik dit kon doen zonder uitsluiting.
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;
De eerste subquery vindt het totale aantal items in elke set en de tweede vindt het aantal overeenkomende items in elke set. Wanneer matches.count
3 is, bevat de set alle items die we zoeken, en als totals.count
is ook 3, de set heeft geen extra items.