Shin: elegante Lösung für Tabellen mit Spaltenversionen

Hallo,

ich arbeite mit mySQL und suche nach einer eleganten Lösung um folgendes Problem zu lösen:

Ich habe eine Art "Objekt" es besitzt eine unbekannte Anzahl an Attributen (in PHP kann man einfach neue definieren). Jedes Attribut repräsentiert eine Spalte eines Tabellen Eintrags. Die Attribute ändern sich aber und bei einer Änderung soll nicht die Spalte geändert werden sondern beide Informationen verfügbar sein und es soll herausfindbarsein, wann die Änderung enstand und welche Werte vorher und nacher drin standen.

Momentan mache ich es so:

page
 id
 (ggf andere feste Spalten)

page_ext
 page_id
 date
 name
 value

Jedes Attribut wird so also als ein Eintrag behandelt in der page_ext und der Page zugeordnet. Name ist der Name des Attribut und value der Inhalt. Wenn der Inhalt sich ändert, wird ein neuer Eintrag gemacht, der anhand des Datums als neuere Version erkannt wird. Ich sehe das nicht als effizieteste Lösung an, weiß jemand wie man so ein Problem "normalerweise" löst?

mfg
Shin

  1. Moin!

    Aus Deinem Posting geht nicht hervor wozu Du das brauchst. Möglich wäre es z.B. bei einem "Update" eines Datensatzes den originären Datensatz zu erst in einer weiteren Tabelle zu archivieren und danach via Update zu verändern.

    Das hält die Datentabelle und vor allem die Indexe klein, was - je nach Aufgabe - unter Performance-Gesichtspunkten ideal sein _kann_.

    Die Archiv-Tabelle hat dann entweder eine zusätzliche Spalte für den Zeitpunkt der Übernahme oder der Zeitpunkt der Eintragung wird in der Original-Tabelle gespeichert und beim Archivieren übernommen. In diesem Fall erhält die Archiv-Tabelle eine weitere ID für Sortierungen etc.  In jedem Fall wird die ID des originalen Datensatzes in eine Spalte geschrieben, welche die Original-Id enthält - die darf dann natürlich weder ein Autoinkrement noch einen Unique-Index haben.

    So ein Archiv macht natürlich vor allem dann Sinn, wenn der Versionsvergleich nicht die Hauptfunktion der gesamten Anwendung ist, sondern in der Hauptsache die aktuelle Version abgefragt wird. Gerade bei der Frage nach Sinnhaftigkeit einer Datenstruktur muss die Frage nach deren Verwendung im Vordergrund stehen. Aber da sind wir wieder beim ersten Satz.

    MFFG (Mit freundlich- friedfertigem Grinsen)

    fastix

    1. Der Sinn dahinter ist ganz einfach, die Änderungungen müssen festgehalten werden und zurücksetzbar sein. Außerdem muss nachvollzogen werden wie und warum sich etwas geändert hat.

      Bei einer Übertragung müsste ich aber den ganzen Satz wieder laden, zwischenspeichern und dann abspeichern.

      Wenn ich noch einen zusätzlichen Index verwende, dann habe ich eine Spalte mehr, wenn ich jedoch das Datum benutzen, schlage ich zwei Fliegen mit einer Klappe einmal welche Version neuer ist und einmal wann die Version erstellt worde.

      1. Hi,

        Wenn ich noch einen zusätzlichen Index verwende, dann habe ich eine Spalte mehr, wenn ich jedoch das Datum benutzen, schlage ich zwei Fliegen mit einer Klappe einmal welche Version neuer ist und einmal wann die Version erstellt worde.

        Zwei Fliegen mit einer Klappe zu schlagen, erfordert aber eine größere Klappe, oder mehr Genauigkeit beim Zielen, oder ... also letzten Endes mehr Aufwand für die Datenbank.

        Mit einer zusätzlichen Spalte, die nur die Information enthält, dass ein Datensatz der aktuellste innerhalb einer bestimmten Gruppierung ist, hast du ggf. bessere Performance beim Auslesen nur der aktiven Datensätze - und wann der aktive Datensatz erstellt wurde, bekommst du über die Datums-Spalte trotzdem als Bonus-Information.

        Informationen über vorherige Versionen kannst du dann immer noch über das Datum alleine beziehen.

        Natürlich erfordert der Umgang mit diesem Aktiv-Kennzeichen etwas mehr Aufwand und Logik und Kontrolle bei der Erstellung und Manipulation der Datnsätze. Aber bei großen Datenbeständen kann es sich durchaus bezahlt machen, dafür auf aufwendige Sortierungen nach einer Datumsspalte beim Auslesen der aktiven Datensätze (die ggf. auch Self JOINs oder Subqueries erfordern) verzichten zu können.

        MfG ChrisB

        --
        RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
        1. okay jetzt erstmal zur eigentlichen Lösung wie würdet ihr die Tabelle aufbauen? Sagen wir wir haben folgendes Objekt:

          entry
           id
           att1
           att2
           att3
           att4

          ich möchte wissen, wann welches att auf was geändert worde. Und JEDE Version muss verfügbar sein.

          Das heißt wenn geändert wird
          att1 = 123
          att1 = 234
          att1 = 485

          sollen alle davon gefunden werden können und das Datum dafür exisiteren. Mometan habe ich ja die name=value lösung, dass heißt bei einer Änderung wird eine neue Spalte in einer zweiten Tabelle erstellet in den name gleich bleibt value aber anders ist und das Datum momentan noch zum sortieren verwendet wird. Da ich aber alle "names" drin speichere kann ich keinen Index definieren den es sind in der Tabelle nicht nur att1 sondern auch att3, att29392 usw drin. Gibt es schon eine Lösung, die effektivier ist? Ich könnte mir vorstellen, dass es so eine Lösung schon gibt z.B. bei Wikipedia, wo es ja auch ALLE Versionen einer Seite noch gibt.

          1. Moin!

            Gibt es schon eine Lösung, die effektivier ist?

            Ja.

            MFFG (Mit freundlich- friedfertigem Grinsen)

            fastix

            1. Okay und wie löse ich das auf sql basis?

              Wenn ich also ne Änderung festelle muss ich ja jede Spalte auslesen in eine $row speichern und dann die Kopie abspeichern in der neuen Tabelle und dann die updates durchführen.

              Das sind 3 SQL Anfragen. Gibs da dann was effizientes?

              1. Moin!

                Das sind 3 SQL Anfragen. Gibs da dann was effizientes?

                Ja. Das MySQL Handbuch zu lesen.

                MFFG (Mit freundlich- friedfertigem Grinsen)

                fastix

                1. Was hälst du von der Lösung die mir gerade in Kopf gekommen ist?
                  Ich habe nur Indexes.

                  Ich habe ein verändertes Objekt und will es speichern, dann suche ich nach den selben Index und sortire nach modified desc. Dann lade ich es und vergleiche beide Objekte, wenn es eine Änderung gibt, dann speicher in den Eintrag einfach mit dem neuen modified Datum. Somit habe ich nur eine Tabelle und kann alles aufeinmal ändern. Blöde nur das es tausende redundanzen gibt, die aber bei deiner Lösung auch exisiterne und nur bei der name=value variante verhindert werden könnten.

                  mfg
                  Shin

                  1. Moin!

                    Ich befinde mich im Ausland und will wissen wie schnell ich fahren darf.
                    In welchem Land?

                    Ich habe ein Auto mit 4 Zylindern.
                    In welchem Land? Fahren Sie innerorts oder außerorts? Auf einer Autobahn?

                    Ich gebe jetzt mal Gas, glauben Sie, das ist richtig?
                    Klick.

                    MFFG (Mit freundlich- friedfertigem Grinsen)

                    fastix

                    1. Also es geht um ein sehr kleines selbstgebautes Communitysystem in den Mitglieder viele Variablen die an Personen/Bilder usw gebunden sind verändern können. Mitglieder sollen nachvolziehen können, wie was und wann es geändert worde. Außerdem sollen Mitglieder mit bestimmten Rechten die "Versionen" wiederherstellen können. Ich lege aber das Hauptaugenmerk in die Speicherung die Informationen werden vom API abgerufen und dann gespeichert.

                      Ein Problem hätte ich aber noch und zwar gleiche ich eine Liste aus Einträgen mit der Datenbank ab und müsste dann die Einträge die in der Liste nicht mehr existieren als gelöscht makieren nur wäre es blöd für jede Version eine gelöscht Spalte zu erstellen, wenn nur der letzte Eintrag damit makiert werden kann.

                      Hoffe das reicht um es zu beurteilen, weil das ganze noch geplant wird.

              2. Achja und nächstes Problem, wie gesagt ist die Anzahl der Attribute unbekannt. Das heißt bei einer zusätlichen Spalte muss ich sowohl 3 SQL Anfragen ändern als auch 2 Tabellen bearbeiten und den Rest in PHP noch bearbeiten...

                1. Moin!

                  Achja und nächstes Problem

                  Wie schon andere vor mir gesagt haben: Die Bequemlichkeit beim Programmieren kostet dann beim Ausführen womöglich Rechenzeit. Nehmen wir einfach an, die Hauptlast liegt auf der Ausgabe der aktuellen Daten, dann ist es sehr von Vorteil, wenn die Datenbank nicht in einem Gesamtarchiv kramen muss um nachzusehen, welche Version der Daten nun die aktuelle ist. Du hast als Kind Dein Lieblingsspielzeug auch nicht eine Kiste getan, in der es mit tausend anderen Sachen zusammen lag sondern für dessen Aufbewahrung einen speziellen Platz gewählt damit Du es schnell greifen kannst.

                  Wenn also Schreiben/Ändern gegenüber dem Abruf des aktuellen Zustandes selten vorkommt, dann solltest Du die Daten fein säuberlich trennen und mit einer Archiv-Tabelle arbeiten um einfach die Last auf dem Server gering zu halten. Das kann sich anders darstellen, wenn z.B. bei einem Versionsverwaltungs-System die Anzahl der schreibenden Änderungen/Vergleiche überwiegt oder derjenigen der nur lesenden Zugriffe sehr nahe kommt - was genau Du gerade programmierst hast Du - trotz Nachfrage - immer noch nicht geschrieben, das ist aber von Interesse um die Last abschätzen zu können.

                  Bei einem solchen System kann es nämlich sogar von Interesse sein die Änderungen zur Vorversion (-> siehe: diff) oder einer Delta-Kodierung zu speichern - um die Datenmenge gering zu halten

                  Auch bei einer Ein-Tabellen-Variante kommst Du mit einer Abfrage übrigens eher nicht hin. Du müsstest, wenn du die Idee verfolgst, den aktuell anzuzeigenden Datensatz markieren - alle anderen erst als ungültig kennzeichnen. Das sind schon zwei Updates - und beide sind mit einer Indexierung verbunden.

                  MFFG (Mit freundlich- friedfertigem Grinsen)

                  fastix