Non commenterò se esiste uno schema più adatto per farlo (è del tutto possibile), ma per uno schema con colonne name
e item
, la query seguente dovrebbe funzionare. (sintassi 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;
L'idea è che abbiamo tutte le chiavi impostate in k
, che poi uniamo ai dati dell'elemento del set in sets
una volta per ogni elemento del set che stiamo cercando, tre in questo caso. Ciascuno dei tre inner join con alias di tabella i1
, i2
e i3
filtra tutti i nomi dei set che non contengono l'elemento cercato con quel join. Infine, abbiamo un join sinistro con sets
con l'alias di tabella ix
, che porta tutti gli oggetti extra nel set, ovvero tutti gli oggetti che non stavamo cercando. ix.name
è NULL
nel caso in cui non vengano trovati elementi extra, che è esattamente quello che vogliamo, quindi il WHERE
clausola. La query restituisce una riga contenente la chiave del set se il set viene trovato, nessuna riga in caso contrario.
Modifica: L'idea alla base della risposta di collapsars sembra essere molto migliore della mia, quindi ecco una versione un po' più breve di quella con una spiegazione.
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;
L'idea qui è quella sottoquery s1
seleziona le chiavi di tutti i set che contengono elementi diversi da quelli che stiamo cercando. Quindi, quando siamo usciti, unisciti a sets
con s1
, s1.name
è NULL
quando il set contiene solo gli articoli che stiamo cercando. Quindi raggruppiamo per chiave di set ed eliminiamo tutti i set con il numero errato di elementi. Rimaniamo quindi solo con set che contengono solo gli articoli che stiamo cercando e sono della lunghezza corretta. Poiché i set possono contenere un elemento solo una volta, può esserci solo un set che soddisfa quei criteri, ed è quello che stiamo cercando.
Modifica: Mi è appena venuto in mente come farlo senza l'esclusione.
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;
La prima sottoquery trova il conteggio totale degli elementi in ogni set e la seconda il conteggio degli elementi corrispondenti in ogni set. Quando matches.count
è 3, il set contiene tutti gli elementi che stiamo cercando e se totals.count
è anche 3, il set non ha elementi extra.