Frage

Ich möchte die Implementierung der Objektverssionierung mit der zusätzlichen Wendung, sowohl live als auch Entwurfsobjekte zu haben, implementieren und könnte die Erkenntnisse von jemandem in dieser Erfahrung nutzen, da ich mich frage, ob es ohne potenziell schreckliche Hacks überhaupt möglich ist.

Ich werde es auf Posts mit Tags ausbrechen, um das Beispiel zu machen, aber mein Anwendungsfall ist etwas allgemeiner (mit langsam ändernden Abmessungen - http://en.wikipedia.org/wiki/slowly_changing_dimension).

Angenommen, Sie haben eine Post -Tabelle, eine Tags -Tabelle und eine Post2TAG -Tabelle:

posts (
 id
)

tags (
 id
)

post2tag (
 post_id fkey posts(id),
 tag_id fkey tags(id)
)

Ich brauche ein paar Dinge:

  1. In der Lage zu sein, genau zu zeigen, wie ein Beitrag in einer willkürlichen DateTime aussah, einschließlich für gelöschte Zeilen.
  2. Behalten Sie den Überblick darüber, wer was bearbeitet, um einen vollständigen Prüfungsweg zu erhalten.
  3. Benötigt eine Reihe materialisierter Ansichten ("Live" -Tabellen), um die referenzielle Integrität beizubehalten (dh die Protokollierung sollte für die Entwickler transparent sein).
  4. Muss für das Leben angemessen schnell sein und Der neueste Entwurfsreihen.
  5. In der Lage sein, einen Entwurf zu einem Live -Post zu haben.

Ich habe verschiedene Optionen untersucht. Bisher sieht das Beste, was ich mir ausgedacht habe (ohne Punkte 4/ #5), ein bisschen wie das SCD-Typ6-Hybrid-Setup aus, aber anstatt einen aktuellen Booleschen zu haben, gibt es eine materialisierte Ansicht für die aktuelle Zeile. In jeder Hinsicht sieht es so aus:

posts (
 id pkey,
 public,
 created_at,
 updated_at,
 updated_by
)

post_revs (
 id,
 rev pkey,
 public,
 created_at,
 created_by,
 deleted_at
)

tags (
 id pkey,
 public,
 created_at,
 updated_at,
 updated_by
)


tag_revs (
 id,
 public,
 rev pkey,
 created_at,
 created_by,
 deleted_at
)

post2tag (
 post_id fkey posts(id),
 tag_id fkey tags(id),
 public,
 created_at,
 updated_at,
 updated_by
)

post2tag_revs (
 post_id,
 tag_id,
 post_rev fkey post_revs(rev), -- the rev when the relation started
 tag_rev fkey tag_revs(rev), -- the rev when the relation started
 public,
 created_at,
 created_by,
 deleted_at,
 pkey (post_rev, tag_rev)
)

Ich verwende PG_Temporal, um die Indizes für die Periode zu verwalten (erstellt_at, deleted_at). Und ich halte die verschiedenen Tabellen mithilfe von Triggern synchron. Yada yada yada ... Ich habe die Auslöser erstellt, die es ermöglichen, eine Bearbeitung für Beiträge/Tags so abzusagen, dass der Entwurf in die Revs gespeichert wird, ohne veröffentlicht zu werden. Es funktioniert großartig.

Außer Wenn ich mir Sorgen um die Beziehungen zwischen Draft und Strow zu Post2TAG machen muss. In diesem Fall bricht die Hölle los, und dieser Hinweis auf mich, dass ich dort ein Designproblem habe. Aber mir geht die Ideen aus ...

Ich habe darüber nachgedacht, Daten Duplikation einzuführen (dh n post2TAG -Zeilen, die für jeden Überarbeitungentwurf eingeführt wurden). Diese Art von funktioniert, ist aber viel langsamer als ich möchte.

Ich habe darüber nachgedacht, Entwürfe für den "letzten Entwurf" einzuführen, aber dies wird schnell sehr, sehr hässlich.

Ich habe alle möglichen Flaggen in Betracht gezogen ...

Also Frage: Gibt es ein allgemein anerkanntes Mittel zur Verwaltung von Live- und Nicht-Live-Zeilen in einer zeilenversion kontrollierten Umgebung? Und wenn nicht, was haben Sie versucht und einigermaßen erfolgreich gewesen?

War es hilfreich?

Lösung 4

Ich glaube, ich habe es geschafft. Grundsätzlich fügen Sie den relevanten Tabellen ein (eindeutiges) Entwurfsfeld hinzu und arbeiten an den Entwürfen, als wären sie ein neuer Beitrag/Tag/etc.:

posts (
 id pkey,
 public,
 created_at stamptz,
 updated_at stamptz,
 updated_by int,
 draft int fkey posts (id) unique
)

post_revs (
 id,
 public,
 created_at,
 created_by,
 deleted_at,
 pkey (id, created_at)
)

tags (
 id pkey,
 public,
 created_at,
 updated_at,
 updated_by,
 draft fkey tags (id) unique
)


tag_revs (
 id,
 public,
 created_at,
 created_by,
 deleted_at,
 pkey (id, created_at)
)

post2tag (
 post_id fkey posts(id),
 tag_id fkey tags(id),
 public,
 created_at,
 updated_at,
 updated_by,
 pkey (post_id, tag_id)
)

post2tag_revs (
 post_id,
 tag_id,
 public,
 created_at,
 created_by,
 deleted_at,
 pkey (post_id, tag_id, created_at)
)

Andere Tipps

Ankermodellierung ist eine gute Möglichkeit, eine temporale DB zu implementieren - siehe das Wikipedia -Artikel zu. Es braucht einige Zeit, um sich daran zu gewöhnen, aber gut arbeiten. Da ist ein Online -Modellierungswerkzeug und wenn Sie die angegebene XML -Datei laden [File -> Load Model from Local File]Sie sollten so etwas sehen - auch verwenden [Layout --> Togle Names].

enter image description here

Das [Generate --> SQL Code] Erstellt DDL für Tabellen, Ansichten und Punkt-in-Zeit-Funktionen. Der Code ist ziemlich lang, also poste ich ihn hier nicht. Überprüfen Sie den Code aus - Sie müssen ihn möglicherweise für Ihre DB ändern.

Hier ist die Datei, die in das Modellierungswerkzeug geladen werden kann.

<schema>
<knot mnemonic="EXP" descriptor="Expired" identity="smallint" dataRange="char(1)">
<identity generator="true"/>
<layout x="713.96" y="511.22" fixed="true"/>
</knot>
<anchor mnemonic="US" descriptor="User" identity="int">
<identity generator="true"/>
<attribute mnemonic="USN" descriptor="UserName" dataRange="varchar(32)">
<layout x="923.38" y="206.54" fixed="true"/>
</attribute>
<layout x="891.00" y="242.00" fixed="true"/>
</anchor>
<anchor mnemonic="PO" descriptor="Post" identity="int">
<identity generator="true"/>
<attribute mnemonic="TIT" descriptor="Title" dataRange="varchar(2)">
<layout x="828.00" y="562.00" fixed="true"/>
</attribute>
<layout x="855.00" y="471.00" fixed="true"/>
</anchor>
<anchor mnemonic="TG" descriptor="Tag" identity="int">
<identity generator="true"/>
<attribute mnemonic="TGT" descriptor="TagText" dataRange="varchar(32)">
<layout x="551.26" y="331.69" fixed="true"/>
</attribute>
<layout x="637.29" y="263.43" fixed="true"/>
</anchor>
<anchor mnemonic="BO" descriptor="Body" identity="int">
<identity generator="true"/>
<attribute mnemonic="BOT" descriptor="BodyText" dataRange="varchar(max)">
<layout x="1161.00" y="491.00" fixed="true"/>
</attribute>
<layout x="1052.00" y="465.00" fixed="true"/>
</anchor>
<tie timeRange="datetime">
<anchorRole role="IsTagged" type="PO" identifier="true"/>
<anchorRole role="IsAttached" type="TG" identifier="true"/>
<anchorRole role="BYAuthor" type="US" identifier="false"/>
<knotRole role="Until" type="EXP" identifier="false"/>
<layout x="722.00" y="397.00" fixed="true"/>
</tie>
<tie timeRange="datetime">
<anchorRole role="Contains" type="PO" identifier="true"/>
<anchorRole role="ContainedIn" type="BO" identifier="false"/>
<layout x="975.00" y="576.00" fixed="true"/>
</tie>
<tie>
<anchorRole role="CreatedBy" type="TG" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="755.10" y="195.17" fixed="true"/>
</tie>
<tie>
<anchorRole role="CreatedBy" type="PO" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="890.69" y="369.09" fixed="true"/>
</tie>
<tie>
<anchorRole role="ModifiedBy" type="BO" identifier="true"/>
<anchorRole role="Author" type="US" identifier="false"/>
<layout x="1061.81" y="322.34" fixed="true"/>
</tie>
</schema>

Ich habe eine zeitliche Datenbank mit SCD Typ 2- und PostgreSQL-Regeln und -auslöschern implementiert und in ein in sich geschlossenes Paket für ActivereCord eingepackt: http://github.com/ifad/chronomodel

Das Design ist jedoch unabhängig von der Sprache / dem Framework - Sie können Regeln und Auslöser manuell erstellen und die Datenbank kümmert sich um den Rest. Sich ansehen https://github.com/ifad/chronomodel/blob/master/readme.sql.

Ebenfalls eine effiziente Indexierung und Abfrage zeitlicher Daten mit geometrischen Operatoren ist als Bonus enthalten. :-)

post2Tag_revs hat ein Problem darin, dass es versucht, 2 grundsätzlich unterschiedliche Konzepte auszudrücken.

Ein Tag, das auf einen Entwurf der Post -Revision angewendet wird, gilt nur für diese eine Revision, es sei denn, die Überarbeitung wird jemals veröffentlicht.

Sobald ein Tag veröffentlicht wurde (dh mit einer veröffentlichten Post -Revision), gilt dies für jede zukünftige Überarbeitung des Beitrags, bis er widerrufen wird.

Und die Zusammenarbeit mit einer veröffentlichten Überarbeitung oder UnaSociating ist nicht unbedingt gleichzeitig mit einer Überarbeitung, die veröffentlicht wird, es sei denn, Sie erzwingen dies künstlich, indem Sie eine Revision klonen, nur damit Sie Tag -Ergänzungen oder -umbauten in Verbindung bringen können ...

Ich würde das Modell ändern, indem ich post2Tag_revs.post_rev nur für Draft -Tags relevant erstellen würde. Sobald die Revision veröffentlicht wurde (und das Tag live), würde ich eine Zeitstempelspalte verwenden, um den Beginn und das Ende der veröffentlichten Gültigkeit zu markieren. Möglicherweise möchten Sie einen neuen post2Tag_revs -Eintrag, der diese Änderung darstellt.

Wie Sie betonen, macht dies diese Beziehung Bi-temporal. Sie können die Leistung im "normalen" Fall verbessern, indem Sie 2TAG einen Booleschen Hinweis hinzufügen, um anzuzeigen, dass das Tag derzeit mit dem Post zugeordnet ist.

Verwenden Sie nur 3 Tabellen: Beiträge, Tags und Post2Tag.

Fügen Sie allen Tabellen Start_Time und End_Time hinzu. Fügen Sie eindeutiger Index für Schlüssel, start_time und end_time hinzu. Fügen Sie eindeutiger Index für Schlüssel hinzu, wobei end_time null ist. Fügen Sie Triger hinzu.

Für Strom:

SELECT ... WHERE end_time IS NULL

Zum Zeitpunkt:

WHERE (SELECT CASE WHEN end_time IS NULL
THEN (start_time <= at_time)
ELSE (start_time <= at_time AND end_time > at_time)
END)

Die Suche nach aktuellen Daten ist aufgrund des Funktionsindex nicht langsam.

Bearbeiten:

CREATE UNIQUE INDEX ... ON post2tag (post_id, tag_id) WHERE end_time IS NULL;
CREATE UNIQUE INDEX ... ON post2tag (post_id, tag_id, start_time, end_time);

FOREIGN KEY (post_id, start_time, end_time) REFERENCES posts (post_id, start_time, end_time) ON DELETE CASCADE ON UPDATE CASCADE;
FOREIGN KEY (tag_id, start_time, end_time) REFERENCES tags (tag_id, start_time, end_time) ON DELETE CASCADE ON UPDATE CASCADE;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top