Filter eine Eins-zu-viele-Abfrage von allen viele treffen Kriterien erfordern
-
20-08-2019 - |
Frage
Stellen Sie sich die folgenden Tabellen:
Tabellenfelder erstellen (id int, namenstext, ...);
Tabelle thingsinboxes (id int, int box_id, was ENUM ( 'Apfel' Banane‘, 'Orange') schaffen;
Und die Tabellen wie folgt aussehen:
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
Wie wähle ich die Boxen, die mindestens eine orange und nichts enthalten, die nicht eine Orange ist?
Wie funktioniert diese Skala, vorausgesetzt, ich mehrere hunderttausend Kisten und möglicherweise eine Million Dinge in Kisten?
Ich möchte das alles, wenn möglich in SQL halten, anstatt Nachverarbeitung das Ergebnis mit einem Skript festgelegt.
ich beide Postgres und MySQL bin mit, so Subqueries wahrscheinlich schlecht ist, da MySQL optimiert nicht Subqueries (pre-Version 6, sowieso).
Lösung
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;
Hier ist eine andere Lösung, die nicht GROUP BY nicht verwendet:
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;
Wie immer, bevor Sie Rückschlüsse auf die Skalierbarkeit oder Leistung einer Abfrage machen, Sie haben es versuchen mit einem realistischen Datensatz und die Leistung messen.
Andere Tipps
ich denke, Bill Karwin Abfrage nur in Ordnung, aber wenn ein relativ geringer Anteil von Kisten Orangen enthält, sollten Sie in der Lage sein, die Dinge, indem Sie einen Index für das thing
Feld zu beschleunigen:
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
Die WHERE NOT EXISTS
Unterabfrage wird nur einmal pro Orange Sache betrieben werden, so ist es nicht zu teuer vorausgesetzt, es gibt nicht viele Orangen.