Jeg har tænkt på det i et stykke tid nu og kan kun komme i tanke om to måder at gøre dette på. Begge kan arbejde fuldt gennemsigtigt, når de er lavet til et abstrakt datalag/model.
I øvrigt er der en implementering for "versionerbare" tabeldata i ORM-mapper-doktrinen. Se dette eksempel i deres dokumenter . Måske passer det til dine behov, men det passer ikke til mit. Det ser ud til at slette alle historiedata, når den originale post slettes, hvilket gør den ikke rigtig revisionssikker.
Mulighed A:Hav en kopi af hver tabel til at opbevare revisionsdata
Lad os sige, at du har en simpel kontakttabel:
CREATE TABLE contact (
id INT NOT NULL auto_increment,
name VARCHAR(255),
firstname VARCHAR(255),
lastname VARCHAR(255),
PRIMARY KEY (id)
)
Du ville oprette en kopi af denne tabel og tilføje revisionsdata:
CREATE TABLE contact_revisions (
id INT NOT NULL,
name VARCHAR(255),
firstname VARCHAR(255),
lastname VARCHAR(255),
revision_id INT auto_increment,
type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
change_time DEFAULT current_timestamp,
PRIMARY KEY(revision_id)
)
Hold styr på INSERT
og UPDATE
ved hjælp af AFTER
udløser. På hver ny datarevision i originalen skal du indsætte en kopi af de nye data i revisionstabellen og indstille modifikations type
korrekt.
For at logge en DELETE
revisionssikker skal du også indsætte en ny række i historiktabellen! Til dette skal du bruge en BEFORE DELETE
udløse og gemme de seneste værdier, før de slettes. Ellers bliver du nødt til at fjerne alle NOT NULL
begrænsning også i historiktabellen.
Nogle vigtige bemærkninger vedrørende denne implementering
- For historiktabellen skal du slette hver
UNIQUE KEY
(her:PRIMARY KEY
) fra revisionstabellen, fordi du vil have den samme nøgle flere gange for hver datarevision. - Når du
ALTER
skemaet og dataene i den originale tabel via en opdatering (f.eks. softwareopdatering), skal du sikre dig, at de samme data eller skemarettelser også anvendes på historietabellen og dens data. Ellers vil du løbe ind i problemer, når du vender tilbage til en ældre revision af et rekordsæt. - I en implementering i den virkelige verden vil du gerne vide, hvilken bruger der har ændret dataene. For at have denne revisionssikker bør en brugerpost aldrig slettes fra brugertabellen. Du skal bare sætte kontoen deaktiveret med et flag.
- Som regel involverer en enkelt brugerhandling mere end én tabel. I en implementering i den virkelige verden skal du også holde styr på, hvilke ændringer i flere tabeller, der hører til en enkelt brugertransaktion, og også i hvilken rækkefølge. I et reelt tilfælde vil du gerne tilbageføre alle ændringer af en enkelt transaktion sammen i en omvendt rækkefølge. Det ville kræve en ekstra revisionstabel, som holder styr på brugerne og transaktioner og holder et løst forhold til alle de individuelle revisioner i historietabellerne.
Fordele:
- helt i databasen, uafhængig af applikationskoden. (vel, ikke når sporing af brugertransaktioner er vigtigt. Det ville kræve noget logik uden for rækkevidden af den enkelte forespørgsel)
- alle data er i deres originale format, ingen implicitte typekonverteringer.
- god præstation på søgning i revisionerne
- nem tilbagerulning. Bare lav en simpel
INSERT .. ON DUPLICATE KEY UPDATE ..
sætning på den originale tabel ved hjælp af dataene fra den revision, du ønsker at rulle tilbage.
Fordele:
- Svært at implementere manuelt.
- Svær (men ikke umulig) at automatisere, når det kommer til databasemigreringer/applikationsopdateringer.
Som allerede nævnt ovenfor, doktriner versionable
gør noget lignende.
Mulighed B:have en central ændringslogtabel
forord:dårlig praksis, vist kun til illustration af alternativet.
Denne tilgang er stærkt afhængig af applikationslogik, som bør være skjult i et datalag/model.
Du har en central historietabel, der holder styr på
- Hvem gjorde det
- hvornår
- rediger, indsæt eller slet
- hvilke data
- i hvilket felt
- af hvilken tabel
Ligesom i den anden tilgang vil du måske også spore, hvilke individuelle dataændringer der hører til en enkelt brugerhandling/transaktion og i hvilken rækkefølge.
Fordele:
- ingen grund til at holde sig synkroniseret med den originale tabel, når du tilføjer felter til en tabel eller opretter en ny tabel. den skaleres gennemsigtigt.
Fordele:
- dårlig praksis ved at bruge en simpel værdi =nøglelager i databasen
- dårlig søgeeffektivitet på grund af implicitte typekonverteringer
- kan forsinke applikationens/databasens overordnede ydeevne, når den centrale historietabel bliver en flaskehals på grund af skrivelåse (dette gælder kun for specifikke motorer med tabellåse, dvs. MyISAM)
- Det er meget sværere at implementere tilbagerulninger
- mulige datakonverteringsfejl/præcisionstab på grund af implicit typekonvertering
- holder ikke styr på ændringer, når du direkte tilgår databasen et sted i din kode i stedet for at bruge din model/datalag og glemmer, at du i dette tilfælde skal skrive til revisionsloggen manuelt. Kan være et stort problem, når du arbejder i et team med andre programmører.
Konklusion:
- Valgmulighed B kan være meget praktisk for små apps som et simpelt "drop in", når det kun er til at logge ændringer.
- Hvis du ønsker at gå tilbage i tiden og let kunne sammenligne forskellene mellem historisk revision 123 til revision 125 og/eller vend tilbage til de gamle data, derefter Valgmulighed A er den svære vej at gå.