Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Gibt es eine MySQL-Option/-Funktion, um den Verlauf von Änderungen an Datensätzen zu verfolgen?

Hier ist eine einfache Möglichkeit, dies zu tun:

Erstellen Sie zunächst eine Verlaufstabelle für jede Datentabelle, die Sie nachverfolgen möchten (Beispielabfrage unten). Diese Tabelle enthält einen Eintrag für jede Einfüge-, Aktualisierungs- und Löschabfrage, die für jede Zeile in der Datentabelle ausgeführt wird.

Die Struktur der Verlaufstabelle ist die gleiche wie die Datentabelle, die sie verfolgt, mit Ausnahme von drei zusätzlichen Spalten:eine Spalte zum Speichern der aufgetretenen Operation (nennen wir sie „Aktion“), das Datum und die Uhrzeit der Operation und eine Spalte zum Speichern einer Sequenznummer ("Revision"), die sich pro Operation erhöht und nach der Primärschlüsselspalte der Datentabelle gruppiert ist.

Um dieses Sequenzierungsverhalten auszuführen, wird ein zweispaltiger (zusammengesetzter) Index für die Primärschlüsselspalte und die Revisionsspalte erstellt. Beachten Sie, dass Sie auf diese Weise nur dann sequenzieren können, wenn die von der Verlaufstabelle verwendete Engine MyISAM ist (Siehe 'MyISAM Notes' auf dieser Seite)

Die Verlaufstabelle ist ziemlich einfach zu erstellen. Ersetzen Sie in der ALTER TABLE-Abfrage unten (und in den Trigger-Abfragen darunter) „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 Trigger:

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, wodurch Sie eine Verlaufstabelle wie diese erhalten (abzüglich der künstlichen „data_columns“-Spalte)

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 Update zu Update anzuzeigen, müssen Sie die Verlaufstabelle in den Primärschlüssel- und Sequenzspalten mit sich selbst verknüpfen. Sie könnten 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

Bearbeiten:Oh wow, die Leute mögen mein Geschichtstabellen-Ding von vor 6 Jahren :P

Meine Umsetzung brummt immer noch vor sich hin, wird größer und unhandlicher, würde ich vermuten. Ich habe Ansichten und eine ziemlich nette Benutzeroberfläche geschrieben, um den Verlauf in dieser Datenbank anzuzeigen, 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 den Kommentaren beschriebenen Probleme vermieden (das Übertragen von Indizes erheblich. 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 buchstabengetreu zu folgen, könnte ein Abenteuer sein, je nachdem, wie etabliert Ihre Datenbank ist.

  • Wenn die Beziehung zwischen dem Primärschlüssel und der Revisionsspalte nicht stimmt, bedeutet dies normalerweise, dass der zusammengesetzte Schlüssel irgendwie gebohrt ist. Bei ein paar seltenen Gelegenheiten passierte mir das und ich war ratlos.

  • Ich fand diese Lösung ziemlich leistungsfähig, da sie Trigger verwendet. Außerdem ist MyISAM schnell bei Inserts, was alles ist, was die Trigger tun. Sie können dies mit intelligenter Indizierung (oder dem Fehlen von ...) weiter verbessern. Das Einfügen einer einzelnen Zeile in eine MyISAM-Tabelle mit einem Primärschlüssel sollte eigentlich keine Operation sein, die Sie optimieren müssen, es sei denn, Sie haben an anderer Stelle erhebliche Probleme. In der ganzen Zeit, in der ich die MySQL-Datenbank ausgeführt habe, war diese Verlaufstabellenimplementierung nie die Ursache für eines der (vielen) auftretenden Leistungsprobleme.

  • 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 nach dem Ausführen mehrerer DML-Aktionen fehlschlagen. Zumindest etwas, dessen man sich bewusst sein 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 hat. Andernfalls schlagen Einfüge-/Aktualisierungs-/Löschabfragen für die 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 großartig, wenn MySQL so etwas wie Schema-Trigger hätte, mit denen Sie die Verlaufstabelle ändern könnten, wenn der Datentabelle Spalten hinzugefügt würden. Hat MySQL das jetzt? Ich reagiere heutzutage :P