sql >> Database teknologi >  >> RDS >> Mysql

Er der en MySQL-indstilling/-funktion til at spore historikken for ændringer i poster?

Her er en enkel måde at gøre dette på:

Først skal du oprette en historiktabel for hver datatabel, du vil spore (eksempel på forespørgsel nedenfor). Denne tabel vil have en post for hver indsæt-, opdaterings- og sletningsforespørgsel udført på hver række i datatabellen.

Strukturen af ​​historiktabellen vil være den samme som den datatabel, den sporer, bortset fra tre yderligere kolonner:en kolonne til at gemme den operation, der fandt sted (lad os kalde det 'handling'), datoen og klokkeslættet for handlingen og en kolonne at gemme et sekvensnummer ('revision'), som stiger pr. operation og er grupperet efter den primære nøglekolonne i datatabellen.

For at gøre denne sekventeringsadfærd oprettes et to-kolonne (sammensat) indeks på den primære nøglekolonne og revisionskolonnen. Bemærk, at du kun kan udføre sekventering på denne måde, hvis motoren, der bruges af historiktabellen, er MyISAM (Se 'MyISAM Notes' på denne side)

Historietabellen er ret nem at oprette. I ALTER TABLE-forespørgslen nedenfor (og i triggerforespørgslerne nedenfor), skal du erstatte "primary_key_column" med det faktiske navn på den kolonne i din datatabel.

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);

Og så opretter du triggerne:

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;

Og du er færdig. Nu vil alle indsættelser, opdateringer og sletninger i 'MyDb.data' blive registreret i 'MyDb.data_history', hvilket giver dig en historietabel som denne (minus den konstruerede 'data_columns'-kolonne)

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 

For at vise ændringerne for en given kolonne eller kolonner fra opdatering til opdatering, skal du slutte historiktabellen til sig selv på den primære nøgle- og sekvenskolonner. Du kan oprette en visning til dette formål, for eksempel:

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, folk kan lide min historie tabel ting fra 6 år siden :P

Min implementering af det nynner stadig med, bliver større og mere uhåndterlig, vil jeg antage. Jeg skrev visninger og en ret fin brugergrænseflade til at se på historien i denne database, men jeg tror aldrig, den blev brugt meget. Så det går.

Sådan behandler du nogle kommentarer i ikke-bestemt rækkefølge:

  • Jeg lavede min egen implementering i PHP, der var lidt mere involveret, og undgik nogle af de problemer, der er beskrevet i kommentarer (få indekser overført, væsentligt. Hvis du overfører unikke indekser til historietabellen, vil tingene gå i stykker. Der er løsninger til dette i kommentarerne). At følge dette indlæg til punkt og prikke kan være et eventyr, afhængigt af hvor etableret din database er.

  • Hvis forholdet mellem den primære nøgle og revisionskolonnen virker off, betyder det normalt, at den sammensatte nøgle er boret på en eller anden måde. Ved nogle få sjældne lejligheder skete det, og jeg var i tvivl om årsagen.

  • Jeg fandt denne løsning temmelig effektiv ved at bruge triggere, som den gør. MyISAM er også hurtig til at indsætte, hvilket er alt, hvad triggerne gør. Du kan forbedre dette yderligere med smart indeksering (eller mangel på...). At indsætte en enkelt række i en MyISAM-tabel med en primær nøgle burde egentlig ikke være en operation, du behøver at optimere, medmindre du har væsentlige problemer andre steder. I hele den tid, jeg kørte MySQL-databasen, var denne historietabelimplementering på, var det aldrig årsagen til nogen af ​​de (mange) ydeevneproblemer, der dukkede op.

  • Hvis du får gentagne indsættelser, skal du kontrollere dit softwarelag for forespørgsler af typen INSERT IGNORE. Hrmm, kan ikke huske nu, men jeg tror, ​​der er problemer med denne ordning og transaktioner, som i sidste ende mislykkes efter at have kørt flere DML-handlinger. Noget man i det mindste skal være opmærksom på.

  • Det er vigtigt, at felterne i historiktabellen og datatabellen stemmer overens. Eller rettere, at din datatabel ikke har FLERE kolonner end historiktabellen. Ellers vil insert/update/del-forespørgsler på datatabellen mislykkes, når indsættelserne til historietabellerne sætter kolonner i forespørgslen, som ikke eksisterer (på grund af d.* i trigger-forespørgslerne), og triggeren mislykkes. Det ville være fantastisk, hvis MySQL havde noget som skema-triggere, hvor du kunne ændre historietabellen, hvis kolonner blev tilføjet til datatabellen. Har MySQL det nu? Jeg reagerer i disse dage :P



  1. Neo4j - Opret en node ved hjælp af Cypher

  2. SQLAlchemy PÅ DUBLIKAT NØGLEOPDATERING

  3. Ydeevne overraskelser og antagelser:DATEADD

  4. jQuery Valider fjernmetodebrug for at kontrollere, om brugernavnet allerede eksisterer