Ecco un modo semplice per farlo:
Innanzitutto, crea una tabella della cronologia per ogni tabella di dati che desideri monitorare (query di esempio di seguito). Questa tabella avrà una voce per ogni query di inserimento, aggiornamento ed eliminazione eseguita su ogni riga nella tabella dati.
La struttura della tabella della cronologia sarà la stessa della tabella dei dati che tiene traccia ad eccezione di tre colonne aggiuntive: una colonna per memorizzare l'operazione che si è verificata (chiamiamola 'azione'), la data e l'ora dell'operazione e una colonna per memorizzare un numero di sequenza ("revisione"), che aumenta per operazione ed è raggruppato in base alla colonna della chiave primaria della tabella dati.
Per eseguire questo comportamento di sequenziamento, viene creato un indice a due colonne (composito) sulla colonna della chiave primaria e sulla colonna della revisione. Nota che puoi eseguire la sequenza in questo modo solo se il motore utilizzato dalla tabella della cronologia è MyISAM ( vedi "Note MyISAM" in questa pagina)
La tabella della cronologia è abbastanza facile da creare. Nella query ALTER TABLE di seguito (e nelle query trigger di seguito), sostituisci "primary_key_column" con il nome effettivo di quella colonna nella tabella dei dati.
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);
E poi crei i 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;
E hai finito. Ora, tutti gli inserimenti, gli aggiornamenti e le eliminazioni in "MyDb.data" verranno registrati in "MyDb.data_history", dandoti una tabella di cronologia come questa (meno la colonna "data_columns" forzata)
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
Per visualizzare le modifiche per una determinata colonna o colonne dall'aggiornamento all'aggiornamento, è necessario unire la tabella della cronologia a se stessa sulla chiave primaria e sulle colonne della sequenza. È possibile creare una vista per questo scopo, ad esempio:
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
Modifica: Oh wow, alle persone piace la mia cosa della tabella di storia di 6 anni fa: P.
La mia implementazione continua a ronzare, diventando più grande e più ingombrante, presumo. Ho scritto visualizzazioni e un'interfaccia utente piuttosto carina per esaminare la cronologia in questo database, ma non credo sia mai stato utilizzato molto. Così è andata.
Per indirizzare alcuni commenti senza un ordine particolare:
Ho realizzato la mia implementazione in PHP che è stata un po 'più complicata ed ho evitato alcuni dei problemi descritti nei commenti (avendo gli indici trasferiti, in modo significativo. Se trasferisci su indici univoci alla tabella della cronologia, le cose si interromperanno. Ci sono soluzioni per questo nei commenti). Seguire questo post alla lettera potrebbe essere un'avventura, a seconda di quanto è consolidato il tuo database.
Se la relazione tra la chiave primaria e la colonna di revisione sembra fuori posto, di solito significa che la chiave composita è in qualche modo bloccata. In alcune rare occasioni mi è capitato questo e non sapevo quale fosse la causa.
Ho trovato questa soluzione piuttosto performante, usando i trigger come fa. Inoltre, MyISAM è veloce negli inserimenti, che è tutto ciò che fanno i trigger. Puoi migliorarlo ulteriormente con l'indicizzazione intelligente (o la mancanza di ...). L'inserimento di una singola riga in una tabella MyISAM con una chiave primaria non dovrebbe essere un'operazione che devi ottimizzare, davvero, a meno che tu non abbia problemi significativi in corso altrove. Per tutto il tempo in cui ho eseguito il database MySQL su cui si trovava l'implementazione della tabella di cronologia, non è mai stata la causa di nessuno dei (molti) problemi di prestazioni che si sono verificati.
se si ottengono inserimenti ripetuti, controllare il livello del software per query di tipo INSERT IGNORE. Hrmm, non ricordo ora, ma penso che ci siano problemi con questo schema e le transazioni che alla fine falliscono dopo aver eseguito più azioni DML. Qualcosa di cui essere consapevoli, almeno.
È importante che i campi nella tabella della cronologia e nella tabella dei dati corrispondano. O, piuttosto, che la tabella dei dati non ha PIÙ colonne della tabella della cronologia. In caso contrario, le query di inserimento / aggiornamento / cancellazione sulla tabella dati falliranno, quando gli inserimenti nelle tabelle cronologiche inseriranno nella query colonne che non esistono (a causa di d. * Nelle query trigger) e il trigger fallirà. Sarebbe fantastico se MySQL avesse qualcosa come i trigger di schema, dove potresti alterare la tabella della cronologia se le colonne fossero aggiunte alla tabella dei dati. MySQL ce l'ha adesso? Reagisco in questi giorni: P