Jag kommer inte kommentera om det finns ett bättre lämpat schema för att göra detta (det är fullt möjligt), utan för ett schema som har kolumner name
och item
, bör följande fråga fungera. (mysql-syntax)
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;
Tanken är att vi har alla inställda nycklar i k
, som vi sedan sammanfogar med set artikeldata i sets
en gång för varje uppsättningsobjekt i uppsättningen vi söker efter, tre i det här fallet. Var och en av de tre inre kopplingarna med tabellalias i1
, i2
och i3
filtrera bort alla uppsättningsnamn som inte innehåller objektet som söktes efter med den kopplingen. Slutligen har vi en vänsterkoppling med sets
med tabellalias ix
, som tar in alla extra föremål i setet, det vill säga varje föremål vi inte letade efter. ix.name
är NULL
i fallet att inga extra föremål hittas, vilket är precis vad vi vill ha, alltså WHERE
klausul. Frågan returnerar en rad som innehåller set-nyckeln om setet hittas, inga rader annars.
Redigera: Tanken bakom kollapsars svar verkar vara mycket bättre än mitt, så här är en lite kortare version av det med förklaring.
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;
Tanken här är att underfrågan s1
väljer nycklarna för alla uppsättningar som innehåller andra objekt än de vi letar efter. Alltså, när vi lämnade gå med i sets
med s1
, s1.name
är NULL
när setet bara innehåller föremål vi söker efter. Vi grupperar sedan efter uppsättningsnyckel och filtrerar bort alla uppsättningar som har fel antal föremål. Vi har då bara set som endast innehåller föremål vi söker efter och som har rätt längd. Eftersom uppsättningar bara kan innehålla ett föremål en gång, kan det bara finnas en uppsättning som uppfyller dessa kriterier, och det är den vi letar efter.
Redigera: Det gick precis upp för mig hur man gör detta utan undantag.
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;
Den första underfrågan hittar det totala antalet objekt i varje uppsättning och den andra tar reda på antalet matchande objekt i varje uppsättning. När matches.count
är 3, har uppsättningen alla artiklar vi letar efter, och om totals.count
är också 3, setet har inga extra föremål.