2012-04-11 11 views
2

Ich habe eine grundlegende CRUD Web App, wo Menschen Artikel erstellen/bearbeiten können. Ich möchte jetzt die Möglichkeit hinzufügen, Revisionshistorien aller Bearbeitungen zu behalten. Derzeit hat ich einen Artikel Tabelle, die wie folgt aussieht:Normalisieren oder Denormalisieren zum Speichern von Revisionsverläufen in einem RDBMS?

Article(id, title, content, author_id, category_id, format) 

I 2 Optionen in Betracht gezogen habe mein aktuelles Schema zum Ändern Unterstützung für Revisionsgeschichte hinzuzufügen. Grundidee ist, dass jede einzelne Bearbeitung für einen Artikel als Datensatz in einer Revisionstabelle gespeichert wird. Also Artikel und Revisionen ist eine Eins-zu-viele-Beziehung.

Erste Option (normalisiert): Eine Tabelle für Artikelmetadaten, eine für Revisionen. Keine doppelten Daten gespeichert.

Article(id, title, category_id) 
Revision(id, content, author_id, format) 

2. Option (de-normalisiert): Zwei Tabellen wie Option 1, aber mit einigen doppelten Spalten.

Article(id, title, content, author_id, category_id, format) 
Revision(id, article_id, content, author_id, format) 

Ich denke, mit der zweiten Option zu gehen, weil es meine Codierung viel einfacher (weniger komplex, weniger Codezeilen) machen. Ich weiß, dass es nicht "akademisch" und "rein" ist, aber mein persönliches Gefühl ist, dass das Hinzufügen von zusätzlichen Joins die Code-Pflege beeinträchtigen würde. Außerdem sollte die Performance besser sein, da nicht so viele Joins durchgeführt werden müssen.

Ist dies ein guter Weg, um diese Aufgabe zu bewältigen? Möglicherweise irgendwelche unvorhergesehenen oder langfristigen Folgen, die ich übersehen habe?

+0

JNK ist richtig (obwohl nicht SQL in SE für Joins optimiert ist - RDBMS sind. Detail aber). Wir haben ein ähnliches Problem für unsere Fakturierungsanwendung, aber dort ist die Tabelle "history" eine exakte Kopie der Rechnungstabelle mit einigen zusätzlichen Feldern (History PK, timestamp, etc). Einfach zu 'INSERT IN HISTORY SELECT NULL, JETZT(), ..., i. * Von Rechnungen i' – Konerak

Antwort

5

Das Leistungsargument ist Unsinn - Sie tun weniger JOIN s, aber RDBMS sind für JOIN s optimiert.

Allerdings ziehen Sie möglicherweise ein Los mehr Daten vom Server als notwendig ist, die nicht weg optimiert werden können.

Sie haben möglicherweise auch eine Konsistenz Problem. Das Duplizieren von Daten für denselben Artikel in verschiedenen Tabellen führt zu Inkonsistenzen. Was ist, wenn die Revisionsdatensätze und der Artikelsatz unterschiedliche Werte für format oder author haben? Woher weißt du, was richtig ist? Was passiert, wenn die content in Articles keiner der Revisionen entspricht?

Sie sollten dies wirklich normalisieren. Ich würde ein CurrentRevision Feld zu Ihrer Articles Tabelle hinzufügen, um mit der gegenwärtigen Version zu verbinden, und Sie sollten eine ArticleID in der Revisions Tabelle haben, um die zwei zusammen zu verbinden.

+0

Vielen Dank für das Licht zu bringen. Ich merke jetzt, dass Code, um Konsistenz zu halten, am Ende mehr Arbeit sein kann. – trinth

+0

Wäre ein CurrentRevision-Feld wirklich notwendig? Es würde 3 Aufrufe an die Datenbank bedeuten, jedes Mal wenn ein Artikel erstellt oder bearbeitet wird: 1. Artikel erstellen 2. Revision mit Bezug auf Artikel aus Schritt (1) erstellen 3. update article.current_revision mit Revision von Schritt (2) – trinth

+1

@trinth Es gibt keinen Grund, dass diese alle separate Anrufe sein müssen. Sie können einen Aufruf tätigen, um den Artikel und die Referenz einzufügen. Sie müssen nur die ID-Werte in Ihrem Code korrekt verarbeiten. – JNK

7

Wenn Sie sich für Ihre Daten interessieren, werden Sie im "denormalisierten" Fall nicht weniger Code haben - Sie müssen erzwingen, dass die letzte Zeile in Revision immer mit der Kopie in Article übereinstimmt. Dies ist in der parallelen Umgebung alles andere als trivial - Sie müssen Ihre Sperre sehr sorgfältig durchführen!

(Wenn Sie Revision und Article nicht die gleiche Kopie enthalten, dann ist dies noch schlimmer - Sie werden auf DBMS angewiesen für die Durchsetzung des Revision Primärschlüssel nicht in der Lage sein!)

Mit einem DBMS mächtig genug, Sie könnten Ihren Kuchen haben und ihn auch essen - zum Beispiel können Oracle materialisierte Ansichten die Daten für Sie "vor-verbinden", ohne das tatsächliche Datenmodell denormalisieren zu müssen.

Auch wenn Sie nicht über ein solches DBMS verfügen, ziehen Sie eine Denormalisierung in Erwägung, nachdem Sie die Leistung auf realistische Datenmengen gemessen haben. Ja, JOINS kann teuer sein, aber sind sie auch teuer in Ihrer besonderen Situation? Nur Messungen können es zeigen. Verwendung Identifizierung Beziehung/natürlichen Schlüssel wie folgt


BTW, betrachten:

enter image description here

Die revision_no wächst monoton wie Sie Revisionen unter dem angegebenen Artikel hinzufügen.

Die B-Tree-Struktur unter dem Revision PK macht es sehr effizient, die neueste (oder irgendeine!) Revision des gegebenen Artikels zu finden. Sofern Sie keine alternativen Schlüssel in Ihrer Frage haben, könnten Sie auch cluster die Revision und (unter Oracle) sogar die Vorderkante des Clustering-Index zu komprimieren, so Raum Overhead von wiederholen article_id wird annulliert.

+0

Ich habe viel von Ihrem Kommentar gelernt, und ich werde mit der normalisierten Option gehen. Ich wähle die andere Antwort als "Lösung", weil sein Vorschlag ist, was ich am Ende verwendet habe. – trinth

+1

@trinth Sei vorsichtig mit 'Article.CurrentRevision'. Vermutlich ist die "Revision" bereits in einem Feld angeordnet, und die letzte Revision kann natürlich aus dieser Reihenfolge abgeleitet werden. "CurrentRevision" führt also keine neuen Informationen in das System ein, es dupliziert nur das vorhandene - es ist ** redundant ** und Redundanzen führen zu Modifikationsanomalien. Sie erhalten nicht einmal einen Leistungsvorteil von seiner Existenz (in einem B-Baum ist die Suche nach MAX so schnell wie die Suche nach konkretem Wert). Seine Existenz ist nur gerechtfertigt, wenn "letzte" und "aktuelle" Revision verschiedene Dinge bedeuten. –