sql >> Databasteknik >  >> RDS >> Mysql

Finns det ett MySQL-alternativ/-funktion för att spåra historik över ändringar i poster?

Här är ett enkelt sätt att göra detta:

Skapa först en historiktabell för varje datatabell du vill spåra (exempelfråga nedan). Den här tabellen kommer att ha en post för varje infoga, uppdatera och radera fråga som utförs på varje rad i datatabellen.

Strukturen för historiktabellen kommer att vara densamma som datatabellen den spårar förutom tre ytterligare kolumner:en kolumn för att lagra operationen som inträffade (låt oss kalla det "åtgärd"), datum och tid för operationen och en kolumn för att lagra ett sekvensnummer ('revision'), som ökar per operation och grupperas efter primärnyckelkolumnen i datatabellen.

För att göra detta sekvenseringsbeteende skapas ett tvåkolumns (sammansatt) index på primärnyckelkolumnen och revisionskolumnen. Observera att du bara kan göra sekvensering på detta sätt om motorn som används av historiktabellen är MyISAM (Se "MyISAM Notes" på den här sidan)

Historietabellen är ganska enkel att skapa. I ALTER TABLE-frågan nedan (och i triggerfrågorna nedanför), ersätt "primary_key_column" med det faktiska namnet på den kolumnen i din datatabell.

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

Och sedan skapar du utlösare:

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;

Och du är klar. Nu kommer alla insättningar, uppdateringar och borttagningar i 'MyDb.data' att registreras i 'MyDb.data_history', vilket ger dig en historiktabell som denna (minus den konstruerade kolumnen '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 

För att visa ändringarna för en viss kolumn eller kolumner från uppdatering till uppdatering, måste du ansluta historiktabellen till sig själv på kolumnerna för primärnyckel och sekvens. Du kan skapa en vy för detta ändamål, till exempel:

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 gillar min historia tabell-grej från 6 år sedan :P

Min implementering av det nynnar fortfarande, blir större och mer otymplig, skulle jag anta. Jag skrev åsikter och ganska trevligt användargränssnitt för att titta på historiken i den här databasen, men jag tror inte att den någonsin använts mycket. Så det går.

För att ta itu med några kommentarer utan särskild ordning:

  • Jag gjorde min egen implementering i PHP som var lite mer involverad, och undvek några av problemen som beskrivs i kommentarerna (att ha index överförda, avsevärt. Om du överför över unika index till historiktabellen kommer saker att gå sönder. Det finns lösningar för detta i kommentarerna). Att följa detta inlägg till punkt och pricka kan vara ett äventyr, beroende på hur etablerad din databas är.

  • Om förhållandet mellan primärnyckeln och revisionskolumnen verkar avstängt betyder det vanligtvis att den sammansatta nyckeln är borrad på något sätt. Vid några få sällsynta tillfällen hade jag detta hänt och var oförstående till orsaken.

  • Jag tyckte att den här lösningen var ganska presterande, med triggers som den gör. MyISAM är också snabb på att infoga, vilket är allt triggers gör. Du kan förbättra detta ytterligare med smart indexering (eller brist på...). Att infoga en enstaka rad i en MyISAM-tabell med en primärnyckel borde egentligen inte vara en operation du behöver optimera, såvida du inte har betydande problem som pågår någon annanstans. Under hela tiden jag körde MySQL-databasen var den här historiktabellimplementeringen på, det var aldrig orsaken till något av de (många) prestandaproblem som uppstod.

  • om du får upprepade infogningar, kontrollera ditt mjukvarulager för frågor av typen INSERT IGNORE. Hrmm, kommer inte ihåg nu, men jag tror att det finns problem med detta schema och transaktioner som slutligen misslyckas efter att ha kört flera DML-åtgärder. Något att vara medveten om, åtminstone.

  • Det är viktigt att fälten i historiktabellen och datatabellen stämmer överens. Eller snarare att din datatabell inte har FLER kolumner än historiktabellen. Annars kommer insert/update/del-frågor i datatabellen att misslyckas, när infogningen i historiktabellerna sätter kolumner i frågan som inte finns (på grund av d.* i triggerfrågorna), och triggern misslyckas. Det skulle vara fantastiskt om MySQL hade något liknande schema-triggers, där du kan ändra historiktabellen om kolumner läggs till i datatabellen. Har MySQL det nu? Jag reagerar nu för tiden :P



  1. MultipleActiveResultSets=Sant eller flera anslutningar?

  2. Jämför strängar som ignorerar accenter i SQL (ORACLE)

  3. Du kan nu använda Access med Microsoft Azure MFA!

  4. BadImageFormatException. Detta inträffar när du kör i 64-bitarsläge med 32-bitars Oracle-klientkomponenter installerade