Hier ist eine einfache Möglichkeit, dies zu tun:
Erstellen Sie zunächst eine Verlaufstabelle für jede Datentabelle, die Sie verfolgen möchten (Beispielabfrage unten). Diese Tabelle enthält einen Eintrag für jede Abfrage zum Einfügen, Aktualisieren und Löschen, die für jede Zeile in der Datentabelle ausgeführt wird.
Die Struktur der Verlaufstabelle entspricht der Datentabelle, die sie verfolgt, mit Ausnahme von drei zusätzlichen Spalten: einer Spalte zum Speichern der aufgetretenen Operation (nennen wir sie "Aktion"), dem Datum und der Uhrzeit der Operation sowie einer Spalte um eine Sequenznummer ('Revision') zu speichern, die pro Operation inkrementiert und nach der Primärschlüsselspalte der Datentabelle gruppiert wird.
Zu diesem Sequenzierungsverhalten wird ein zweispaltiger (zusammengesetzter) Index für die Primärschlüsselspalte und die Revisionsspalte erstellt. Beachten Sie, dass Sie die Sequenzierung nur auf diese Weise durchführen können, wenn die von der Verlaufstabelle verwendete Engine MyISAM ist ( siehe 'MyISAM-Hinweise' auf dieser Seite).
Die Verlaufstabelle ist ziemlich einfach zu erstellen. Ersetzen Sie in der unten stehenden ALTER TABLE-Abfrage (und in den darunter liegenden Trigger-Abfragen) 'primary_key_column' durch den tatsächlichen Namen dieser Spalte in Ihrer Datentabelle.
CREATE TABLE MyDB.data_history LIKE MyDB.data;
ALTER TABLE MyDB.data_history MODIFY COLUMN primary_key_column int(11) NOT NULL,
DROP PRIMARY KEY, ENGINE = MyISAM, ADD action VARCHAR(8) DEFAULT 'insert' FIRST,
ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER action,
ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
ADD PRIMARY KEY (primary_key_column, revision);
Und dann erstellen Sie die Auslöser:
DROP TRIGGER IF EXISTS MyDB.data__ai;
DROP TRIGGER IF EXISTS MyDB.data__au;
DROP TRIGGER IF EXISTS MyDB.data__bd;
CREATE TRIGGER MyDB.data__ai AFTER INSERT ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'insert', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__au AFTER UPDATE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'update', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__bd BEFORE DELETE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'delete', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = OLD.primary_key_column;
Und du bist fertig. Jetzt werden alle Einfügungen, Aktualisierungen und Löschungen in 'MyDb.data' in 'MyDb.data_history' aufgezeichnet, sodass Sie eine Verlaufstabelle wie diese erhalten (abzüglich der erfundenen Spalte 'data_columns').
ID revision action data columns..
1 1 'insert' .... initial entry for row where ID = 1
1 2 'update' .... changes made to row where ID = 1
2 1 'insert' .... initial entry, ID = 2
3 1 'insert' .... initial entry, ID = 3
1 3 'update' .... more changes made to row where ID = 1
3 2 'update' .... changes made to row where ID = 3
2 2 'delete' .... deletion of row where ID = 2
Um die Änderungen für eine bestimmte Spalte oder Spalten von Aktualisierung zu Aktualisierung anzuzeigen, müssen Sie die Verlaufstabelle in den Primärschlüssel- und Sequenzspalten mit sich selbst verknüpfen. Sie können zu diesem Zweck eine Ansicht erstellen, zum Beispiel:
CREATE VIEW data_history_changes AS
SELECT t2.dt_datetime, t2.action, t1.primary_key_column as 'row id',
IF(t1.a_column = t2.a_column, t1.a_column, CONCAT(t1.a_column, " to ", t2.a_column)) as a_column
FROM MyDB.data_history as t1 INNER join MyDB.data_history as t2 on t1.primary_key_column = t2.primary_key_column
WHERE (t1.revision = 1 AND t2.revision = 1) OR t2.revision = t1.revision+1
ORDER BY t1.primary_key_column ASC, t2.revision ASC
Edit: Oh wow, Leute mögen meine Geschichtstabelle vor 6 Jahren: P.
Ich würde annehmen, dass meine Implementierung immer noch summt und größer und unhandlicher wird. Ich habe Ansichten und eine hübsche Benutzeroberfläche geschrieben, um den Verlauf in dieser Datenbank zu betrachten, aber ich glaube nicht, dass sie jemals viel verwendet wurde. So geht es.
Um einige Kommentare in keiner bestimmten Reihenfolge anzusprechen:
Ich habe meine eigene Implementierung in PHP durchgeführt, die etwas komplizierter war, und einige der in Kommentaren beschriebenen Probleme vermieden (Indizes wurden signifikant übertragen. Wenn Sie eindeutige Indizes in die Verlaufstabelle übertragen, werden die Dinge kaputt gehen. Es gibt Lösungen für dies in den Kommentaren). Diesem Beitrag auf den Brief zu folgen, könnte ein Abenteuer sein, abhängig davon, wie etabliert Ihre Datenbank ist.
Wenn die Beziehung zwischen dem Primärschlüssel und der Revisionsspalte nicht korrekt zu sein scheint, bedeutet dies normalerweise, dass der zusammengesetzte Schlüssel irgendwie verzerrt ist. In einigen seltenen Fällen hatte ich dies und war für die Sache ratlos.
Ich fand diese Lösung ziemlich performant und verwendete dabei Trigger. Außerdem ist MyISAM bei Einfügungen schnell, was alles ist, was die Trigger tun. Sie können dies durch intelligente Indizierung (oder fehlende ...) weiter verbessern. Das Einfügen einer einzelnen Zeile in eine MyISAM-Tabelle mit einem Primärschlüssel sollte kein Vorgang sein, den Sie wirklich optimieren müssen, es sei denn, Sie haben an anderer Stelle erhebliche Probleme. Während der gesamten Zeit, in der ich die MySQL-Datenbank ausführte, in der sich diese Implementierung der Verlaufstabelle befand, war dies nie die Ursache für eines der (vielen) Leistungsprobleme, die auftraten.
Wenn Sie wiederholt Einfügungen erhalten, überprüfen Sie Ihre Softwareschicht auf Abfragen vom Typ INSERT IGNORE. Hrmm, ich kann mich jetzt nicht erinnern, aber ich denke, es gibt Probleme mit diesem Schema und Transaktionen, die letztendlich fehlschlagen, nachdem mehrere DML-Aktionen ausgeführt wurden. Zumindest etwas, das man beachten sollte.
Es ist wichtig, dass die Felder in der Verlaufstabelle und der Datentabelle übereinstimmen. Oder vielmehr, dass Ihre Datentabelle nicht MEHR Spalten als die Verlaufstabelle enthält. Andernfalls schlagen Einfügen / Aktualisieren / Löschen von Abfragen in der Datentabelle fehl, wenn die Einfügungen in die Verlaufstabellen Spalten in die Abfrage einfügen, die nicht vorhanden sind (aufgrund von d. * In den Triggerabfragen), und der Trigger fehlschlägt. Es wäre fantastisch, wenn MySQL so etwas wie Schema-Trigger hätte, bei denen Sie die Verlaufstabelle ändern könnten, wenn Spalten zur Datentabelle hinzugefügt würden. Hat MySQL das jetzt? Ich reagiere heutzutage: P.