Doppelte Spalte für schnellere Abfragen?

30

Der Titel macht nicht allzu viel Sinn, aber ich könnte mir keinen besseren Titel für dieses Problem vorstellen.

Ich habe die folgenden Tabellen

Projekte

  • Ich würde
  • Name

Kunden

  • Ich würde
  • id_project
  • Name

Zahlungen

  • Ich würde
  • id_customer
  • Datum
  • Summe

Wenn ein Benutzer das System betritt, hat er Zugriff auf ein bestimmtes Projekt. Jetzt möchte ich alle Zahlungen für dieses Projekt auflisten, und es sollte ziemlich einfach sein:

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

Meine Frage ist: Wenn es nicht besser ist, der Zahlungstabelle auf diese Weise eine Spalte id_project hinzuzufügen, werden die Abfragen einfacher und schneller.

Gabriel Solomon
quelle
1
Daher ist die Abfrage für moderne RDBMS kein Problem (oder besser, verwenden Sie die Verknüpfung).
Garik
4
Stimmen Sie zu, holen Sie sich einen Abfrageplan für Subselect vs Join und finden Sie heraus, welcher besser ist
Gaius
1
Ich denke , das SO Post wert suchen, da @igor erwähnt über die Verwendung von JOIN oder IN
CoderHawk

Antworten:

52

Sie scheinen zu fragen, ob eine Denormalisierung sinnvoll ist.

Bei der Denormalisierung wird versucht, die Leseleistung einer Datenbank durch Hinzufügen redundanter Daten oder Gruppieren von Daten zu optimieren. In einigen Fällen hilft die Denormalisierung, die Ineffizienzen der relationalen Datenbanksoftware zu vertuschen. Bei einer relational normalisierten Datenbank ist die physische Speicherung von Daten mit einer hohen Zugriffslast verbunden, selbst wenn sie für eine hohe Leistung optimiert ist.

Die Antwort lautet immer "es kommt darauf an", also hier ist meine Faustregel:

Ob ...

  • Die Datenmenge ist nicht groß
  • Du machst nicht schon eine Menge Joins
  • und / oder Datenbankleistung ist derzeit kein Engpass

dann normalisiert bleiben . Ja, Denormalisierung ist schneller, aber es bedeutet auch, dass Sie redundante Daten im System haben - Daten, die gepflegt und synchron gehalten werden müssen. Es gibt nicht mehr "eine Quelle" für diese Daten, sondern mehrere Quellen, die abweichen können. Dies ist mit der Zeit riskant, daher sollten Sie es nicht tun, es sei denn, Sie haben sehr gute Gründe, dies zu tun, die durch einige Benchmarks untermauert werden.

Ich würde nur denormalisieren, wenn ...

  • Die Datenmenge ist sehr groß
  • Joins sind teuer und Sie müssen eine Menge von ihnen tun, um auch triviale Abfragen zurück zu bekommen
  • Die Datenbankleistung ist ein Engpass und / oder Sie möchten so schnell wie möglich arbeiten

Joins sind auf moderner Hardware sehr schnell, aber niemals kostenlos.

Jeff Atwood
quelle
9

Sie sollten die Abfrage folgendermaßen umschreiben:

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

Während dies weniger präzise erscheint und ein guter Abfrageplaner erkennt, was Sie versuchen, und Ihre korrelierte Unterabfrage stattdessen als obiger Join ausführt, führt ein schlechter Abfrageplan möglicherweise einen Index-Scan durch payments.id_customer(vorausgesetzt, Sie haben einen relevanten Index) ) (oder schlimmer noch, das Scannen von Tabellen), anstatt die Dinge auf effizientere Weise zu erledigen. Selbst ein guter Abfrageplaner kann die Optimierung möglicherweise nicht erkennen, wenn die Anordnung dieser Abfrage etwas komplizierter ist. Das Ausdrücken der Beziehung als Join und nicht als Unterabfrage kann mehr bewirken als das Ändern Ihrer Datenstruktur.

Wie Jeff sagt, sollte jede Denormalisierung mit Vorsicht in Betracht gezogen werden - dies kann zu leichten Leistungssteigerungen führen, insbesondere für einige Berichtszwecke, kann jedoch zu Inkonsistenzen aufgrund von Fehlern in der unterstützenden Geschäftslogik führen.

Als Randnotiz: Natürlich kenne ich Ihr Geschäft nicht, daher könnte mir etwas fehlen, aber Ihre Tischbeziehungen scheinen mir seltsam. Sie implizieren, dass Sie nie mehr als ein Projekt mit demselben Kunden haben können, was meiner Erfahrung nach normalerweise nicht der Fall ist, zumindest nicht über einen längeren Zeitraum.

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

oder wenn es weniger normalisiert ist (obwohl ich bezweifle, dass dies notwendig wäre):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

Das schließt natürlich die Möglichkeit eines gemeinsamen Projekts mit zwei Kunden aus ...

David Spillett
quelle
3
Erste Leistungsregel: Niemals * in der Produktion verwenden!
Brian Ballsun-Stanton
@ Brian: sehr gültiger Punkt. Und neben den möglichen Auswirkungen auf die Leistung werden durch die Vermeidung von * in Select-Klauseln auch Probleme mit der Spaltenreihenfolge in Views-on-View in MSSQL vermieden, wenn sys.depends aus dem Ruder läuft, weil DROP VIEW+ CREATE VIEWanstelle von verwendet wird ALTER VIEW.
David Spillett
@ Brian, den ich * für das einfache Schreiben setzte.
Gabriel Solomon
Das Projekt ist eher eine unabhängige Anwendung mit einer On-Domain und gehört verschiedenen Kunden, sodass ein Kunde nicht dasselbe Konto für verschiedene Projekte haben kann
Gabriel Solomon
4

In einigen Datenbanken haben Sie die Möglichkeit, "Materialisierte Ansichten" anstelle komplexer ANSICHTEN mit einer großen Datenmenge auf der Grundlage einer komplexen Abfrage zu erstellen. Dies kann verwendet werden, um eine Denormalisierung in einem historisch gewachsenen Anwendungssystem zu vermeiden. Materialisierte Ansichten "Sie müssen eine klare Vorstellung von den Aktualisierungsmethoden und der Menge des Speichers haben, der von der Materialisierten Ansicht verwendet wird ...

Christof Prettner
quelle