Filtra una query uno-a-molti richiedendo che molti di essi soddisfino i criteri
-
20-08-2019 - |
Domanda
Immagina le seguenti tabelle:
crea caselle di tabella (id int, nome testo, ...);
crea oggetti da tavolo (id int, box_id int, thing enum ('apple,' banana ',' orange ');
E le tabelle sembrano:
Boxes: id | name 1 | orangesOnly 2 | orangesOnly2 3 | orangesBananas 4 | misc thingsinboxes: id | box_id | thing 1 | 1 | orange 2 | 1 | orange 3 | 2 | orange 4 | 3 | orange 5 | 3 | banana 6 | 4 | orange 7 | 4 | apple 8 | 4 | banana
Come seleziono le caselle che contengono almeno un'arancia e niente che non sia un'arancia?
In che modo questa scala, supponendo che io abbia diverse centinaia di migliaia di scatole e forse un milione di cose in scatole?
Vorrei mantenere tutto questo in SQL, se possibile, piuttosto che post-elaborazione del set di risultati con uno script.
Sto usando sia Postgres che MySQL, quindi le sottoquery sono probabilmente pessime, dato che mysql non ottimizza le subquery (pre-versione 6, comunque).
Soluzione
SELECT b.*
FROM boxes b JOIN thingsinboxes t ON (b.id = t.box_id)
GROUP BY b.id
HAVING COUNT(DISTINCT t.thing) = 1 AND SUM(t.thing = 'orange') > 0;
Ecco un'altra soluzione che non utilizza GROUP BY:
SELECT DISTINCT b.*
FROM boxes b
JOIN thingsinboxes t1
ON (b.id = t1.box_id AND t1.thing = 'orange')
LEFT OUTER JOIN thingsinboxes t2
ON (b.id = t2.box_id AND t2.thing != 'orange')
WHERE t2.box_id IS NULL;
Come sempre, prima di trarre conclusioni sulla scalabilità o sulle prestazioni di una query, devi provarla con un set di dati realistico e misurare le prestazioni.
Altri suggerimenti
Penso che la query di Bill Karwin vada bene, tuttavia se una percentuale relativamente piccola di scatole contiene arance, dovresti essere in grado di velocizzare le cose usando un indice nel campo thing
:
SELECT b.*
FROM boxes b JOIN thingsinboxes t1 ON (b.id = t1.box_id)
WHERE t1.thing = 'orange'
AND NOT EXISTS (
SELECT 1
FROM thingsinboxes t2
WHERE t2.box_id = b.id
AND t2.thing <> 'orange'
)
GROUP BY t1.box_id
La sottoquery WHERE NOT EXISTS
verrà eseguita una sola volta per dispositivo arancione, quindi non è troppo costosa a condizione che non ci siano molte arance.