Ist die Verwendung mehrerer durch Komma getrennter Fremdschlüssel falsch, und wenn ja, warum?

31

Es gibt zwei Tabellen: Dealund DealCategories. Ein Geschäft kann viele Geschäftskategorien haben.

Daher sollte der richtige Weg darin bestehen, eine Tabelle DealCategoriesmit der folgenden Struktur aufzurufen:

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)

Unser Outsourcing-Team hat die verschiedenen Kategorien jedoch folgendermaßen in der DealTabelle gespeichert :

DealId (PK)
DealCategory -- In here they store multiple deal ids separated by commas like this: 18,25,32.

Ich habe das Gefühl, dass das, was sie getan haben, falsch ist, aber ich weiß nicht genau, warum dies nicht richtig ist.

Wie soll ich ihnen erklären, dass das falsch ist? Oder vielleicht bin ich derjenige, der sich irrt und das ist akzeptabel?

Sarawut Positwinyu
quelle
20
Du hast recht. Ist das Speichern einer durch Kommas getrennten Liste in einer Datenbankspalte wirklich so schlimm? . Kurze Antwort: Ja, so schlimm ist es.
ypercubeᵀᴹ
7
Feuer das ausgelagerte Team sofort, bevor sie mehr Schaden anrichten ... (-_-)
Rafa

Antworten:

49

Ja, das ist eine schreckliche Idee.

Anstatt zu gehen:

SELECT Deal.Name, DealCategory.Name
FROM Deal
  INNER JOIN
     DealCategories ON Deal.DealID = DealCategories.DealID
  INNER JOIN
     DealCategory ON DealCategories.DealCategoryID = DealCategory.DealCategoryID
WHERE Deal.DealID = 1234

Du musst jetzt gehen:

SELECT Deal.ID, Deal.Name, DealCategories
FROM Deal
WHERE Deal.DealID = 1234

Dann müssen Sie Ihren Anwendungscode bearbeiten, um diese Kommaliste in einzelne Zahlen aufzuteilen, und dann die Datenbank separat abfragen:

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)

Dieses Design-Antimuster beruht entweder auf einem völligen Missverständnis der relationalen Modellierung (Sie müssen keine Angst vor Tabellen haben. Tabellen sind Ihre Freunde. Verwenden Sie sie) oder auf einer bizarren falschen Überzeugung, dass es schneller ist, eine durch Kommas getrennte Liste zu erstellen und zu teilen im Anwendungscode als es ist, eine Verknüpfungstabelle hinzuzufügen (es ist nie ). Die dritte Möglichkeit ist, dass sie nicht sicher / kompetent genug mit SQL sind, um Fremdschlüssel einrichten zu können. In diesem Fall sollten sie jedoch nichts mit dem Entwurf eines relationalen Modells zu tun haben.

SQL Antipatterns (Karwin, 2010) widmet diesem Antipattern (das er "Jaywalking" nennt) auf den Seiten 15-23 ein ganzes Kapitel. Auch der Autor hat über eine ähnliche Frage bei SO gepostet . Wichtige Punkte, die er notiert (wie auf dieses Beispiel angewendet), sind:

  • Das Abfragen aller Deals in einer bestimmten Kategorie ist ziemlich kompliziert (der einfachste Weg, dieses Problem zu lösen, ist ein regulärer Ausdruck, aber ein regulärer Ausdruck ist ein Problem an sich).
  • Ohne Fremdschlüsselbeziehungen können Sie keine referenzielle Integrität erzwingen. Wenn Sie DealCategory nr löschen. # 26 müssen Sie dann in Ihrem Anwendungscode jedes Geschäft nach Verweisen auf Kategorie # 26 durchsuchen und diese löschen. Dies ist etwas, das auf der Datenebene gehandhabt werden sollte, und es ist eine sehr schlechte Sache, in Ihrer Anwendung damit umgehen zu müssen .
  • Aggregatabfragen ( COUNT, SUMusw.) wieder variieren von ‚kompliziert‘ zu ‚fast unmöglich‘. Fragen Sie Ihre Entwickler, wie Sie eine Liste aller Kategorien mit der Anzahl der Deals in dieser Kategorie erhalten. Bei korrektem Design sind das vier Zeilen SQL.
  • Aktualisierungen werden viel schwieriger (dh Sie haben einen Deal in fünf Kategorien, möchten jedoch zwei entfernen und drei weitere hinzufügen). Das sind drei Zeilen SQL mit einem richtigen Design.
  • Irgendwann stoßen Sie auf VARCHARLängenbeschränkungen für Listen. Wenn Sie eine durch Kommas getrennte Liste mit mehr als 4000 Zeichen haben, analysieren Sie wahrscheinlich, dass das Monster sowieso langsam ist.
  • Das Abrufen einer Liste aus der Datenbank, das Aufteilen der Liste und das anschließende Zurückkehren zur Datenbank für eine andere Abfrage ist wesentlich langsamer als eine Abfrage.

TLDR: Es handelt sich um ein grundlegend fehlerhaftes Design, das sich nicht gut skalieren lässt, selbst bei einfachsten Abfragen zusätzliche Komplexität bietet und Ihre Anwendung sofort verlangsamt.

Simon Righarts
quelle
1
Simon, jemand hat die gleiche Frage gestellt ( dba.stackexchange.com/questions/17824/… ), aber ich habe keine Ahnung , warum derselbe FK und derselbe PK in derselben Tabelle stehen, die den 3FN bremsen.
jcho360
2
Ich war mir nicht ganz sicher, ob sie eine Beziehung zwischen Deals und Categories oder eine Art Kategorienhierarchie haben wollten. In beiden Fällen war dies eine Nebensache, da durch Kommas getrennte Felder anstelle einer Verknüpfungstabelle keine gute Idee sind.
Simon Righarts
4

Unser Outsourcing-Team hat die verschiedenen Kategorien jedoch folgendermaßen in der Deal-Tabelle gespeichert:

DealId (PK) DealCategory - Hier werden mehrere durch Kommas getrennte Deal-IDs gespeichert: 18,25,32.

Das ist eigentlich ein gutes Design, wenn Sie nur die Kategorien für ein bestimmtes Geschäft abfragen müssen.

Aber es ist schrecklich, wenn Sie alle Angebote in einer bestimmten Kategorie wissen wollen.

Außerdem ist es sehr schwierig und fehleranfällig, etwas anderes zu tun - wie Aktualisierungen, Zählungen, Verknüpfungen usw.

Denormalisierung hat ihren Platz, aber Sie müssen bedenken, dass sie für eine Art von Abfrage auf Kosten aller anderen optimiert wird, die Sie möglicherweise für dieselben Daten durchführen. Wenn Sie wissen, dass Sie immer in einem Muster abfragen, bietet es möglicherweise einen Vorteil, das denormalisierte Design zu verwenden. Wenn Sie bei den Abfragetypen jedoch mehr Flexibilität benötigen, sollten Sie sich an ein normalisiertes Design halten.

Wie bei jeder anderen Form der Optimierung müssen Sie wissen, welche Abfragen Sie ausführen werden, bevor Sie entscheiden können, ob die Denormalisierung gerechtfertigt ist.

Bill Karwin
quelle
1
Denken Sie wirklich, dass eine Zeichenfolge mit durch Kommas getrennten untergeordneten IDs hilfreich ist? Ich meine, die Anwendung musste zuerst lesen, dann die IDs analysieren und alle Kinder abfragen, wie select * from DealCategories where DealId in (1,2,3,4,...). Sie haben mehr Erfahrung mit Datenbankdesign als ich. Vielleicht haben Sie in einigen Fällen gute Gründe für solch ein "extremes Tuning" in ganz bestimmten Fällen. Meine einzige Idee, dies zu rechtfertigen, ist eine sehr hohe selectBelastung von Deal / DealCategory. Für mich ähnelt dies einem Outsourcing-Team, das ohne DB-Design-Kenntnisse Tabellen erstellt hat.
Erik Hart
1
@ErikHart, das ist Denormalisierung, und es kann hilfreich sein, aber mein Punkt ist, dass es ganz von den Abfragen abhängt, die Sie ausführen müssen. Sie haben Recht, dass durch die Denormalisierung alle Abfragen schlechter ausgeführt werden, mit Ausnahme derjenigen, für die sie optimiert wurden. Wenn Sie nur diese eine Abfrage ausführen müssen und sich nicht für die anderen Abfragen interessieren, ist dies ein Gewinn. Dies ist jedoch in seltenen Fällen der Fall, da wir in der Regel eine flexible Abfrage der Daten auf verschiedene Arten wünschen.
Bill Karwin
1
@ErikHart, wenn das Outsourcing-Team Projektspezifikationen erhalten hätte, die nur eine Abfrage für diese Daten enthielten, hätte es eine Optimierung nur für diese bestimmte Abfrage entwerfen können. Mit anderen Worten: "Sie haben danach gefragt, Sie haben es verstanden." Der Outsourcing-Anbieter hat jedoch keinen Grund, eine künftige Verwendung der Daten zu planen. Er implementiert die Anwendung auf den Buchstaben, der in der Spezifikation steht.
Bill Karwin
1

Mehrere Werte in einer Spalte entsprechen der 1. Normalform.

Es ist auch absolut kein Geschwindigkeitsgewinn, da die Tabellen in der Datenbank verlinkt werden sollen. Sie müssen zuerst eine Zeichenfolge lesen und analysieren und dann alle Kategorien für den "Deal" auswählen.

Die korrekte Implementierung wäre eine Junction-Tabelle wie "DealDealCategories" mit DealId und DealCategoryId.

Schlechte Hierarchieimplementierung?

Außerdem sieht eine FK in DealCategories zu einer anderen DealCategory wie eine schlechte Implementierung einer Hierarchie / eines Baums von DealCategories aus. Die Arbeit mit Bäumen über eine Eltern-ID-Beziehung (so genannte Adjazenzliste) ist ein Schmerz!

Achten Sie beim Implementieren von Hierarchien auf verschachtelte Sets (gut zu lesen, aber schwer zu ändern) und Closure Tables (beste Gesamtleistung, aber möglicherweise hohe Speichernutzung - wahrscheinlich nicht zu viel für Ihre DealCategories)!

Erik Hart
quelle