Der reinen Datenbank-Lehre nach ist es strikt verboten mehrere Werte
auf einem Attribut zu speichern. Sowas kommt in der Realität trotzdem
vor, sogar bei Fremdschlüssel-Attributen. D.h. das
Fremdschlüssel-Attribut referenziert in dem Fall nicht nur eine,
sondern mehrere Zeilen. Die gute Nachricht ist, dass MySQL es
erlaubt, für so ein - ansich unterlaubtes - Design mithilfe der
Funktion find_in_set() eine Join-Condition zu formulieren.
Beispiel
Gegeben zwei Tabellen TableA und TableB, wobei Attribut
TableB.tablea_id auf mehrere Zeilen in TableA verweist.
TableA TableB
id id tablea_id
-- -- ---------
1 1 2,3
2 2 3
3 3 1,2,3,4
4 4 NULL
Wäre TableB.tablea_id ein normales Fremdschlüssel-Attribut mit
einem Wert sähe die Join-Condition so aus:
TableA.id = TableB.tablea_id
Diese Bedingung ist hier nicht anwendbar, da eine Identität (=)
zwischen TableA.id und TableB.tablea_id nur manchmal gegeben ist.
Die Selektion
SELECT
b.id b_id
, a.id a_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.tablea_id
ORDER BY
b.id
, a.id
liefert ein falsches Resultat
+------+------+
b_id | a_id |
+------+------+
| 1 | 2 | zweifelhaft
| 2 | 3 | erwartet
| 3 | 1 | zweifelhaft
+------+------+
Da MySQL bei numerischen Identitäts-Vergleichen eine laxe Auffassung
hat und gegen den numerischen Anfang einer Zeichenkette vergleicht,
auch wenn die Zeichenkette insgesamt keine Zahl darstellt, ist die
Ergebnismenge zusätzlich zweifelhaft. Statt der erwarten einen
Zeile werden drei Zeilen geliefert.
MySQL besitzt jedoch eine Funktion find_in_set(), mit deren Hilfe
eine Join-Condition formuliert werden kann, die die mehrwertigen
Verweise korrekt auflöst:
FIND_IN_SET(TableA.id, TableB.tablea_id) > 0
Die Selektion
SELECT
b.id b_id
, a.id a_id
FROM
TableA a
INNER JOIN TableB b
ON FIND_IN_SET(a.id, b.tablea_id) > 0
ORDER BY
b.id
, a.id
liefert das korrekte Resultat
+------+------+
b_id | a_id |
+------+------+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
| 3 | 4 |
+------+------+
Links