Ik denk daar al een tijdje over na en kan maar twee manieren bedenken om dit te doen. Beide kunnen volledig transparant werken wanneer ze worden verwerkt tot een abstracte gegevenslaag/model.
Trouwens, er is een implementatie voor "versiebare" tabelgegevens in de ORM-mapperdoctrine. Zie dit voorbeeld in hun documenten . Misschien past dat bij jouw behoeften, maar niet bij de mijne. Het lijkt alle geschiedenisgegevens te verwijderen wanneer het originele record wordt verwijderd, waardoor het niet echt revisieveilig is.
Optie A:een kopie van elke tabel hebben om revisiegegevens te bewaren
Stel dat u een eenvoudige contacttabel heeft:
CREATE TABLE contact (
id INT NOT NULL auto_increment,
name VARCHAR(255),
firstname VARCHAR(255),
lastname VARCHAR(255),
PRIMARY KEY (id)
)
U zou een kopie van die tabel maken en revisiegegevens toevoegen:
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)
)
Blijf op de hoogte van INSERT
en UPDATE
met behulp van AFTER
triggers. Voeg bij elke nieuwe gegevensrevisie in het origineel een kopie van de nieuwe gegevens in de revisietabel in en stel de wijziging in op type
correct.
Een DELETE
loggen revisieveilig moet u ook een nieuwe rij in de geschiedenistabel invoegen! Gebruik hiervoor een BEFORE DELETE
trigger en sla de laatste waarden op voordat ze worden verwijderd. Anders moet u elke NOT NULL
. verwijderen beperking ook in de geschiedenistabel.
Enkele belangrijke opmerkingen over deze implementatie
- Voor de geschiedenistabel moet je elke
UNIQUE KEY
(hier:dePRIMARY KEY
) uit de revisietabel omdat u dezelfde sleutel meerdere keren zult hebben voor elke gegevensrevisie. - Als je
ALTER
het schema en de gegevens in de oorspronkelijke tabel via een update (bijv. software-update) moet u ervoor zorgen dat dezelfde gegevens of schemacorrecties ook worden toegepast op de geschiedenistabel en de bijbehorende gegevens. Anders kom je in de problemen als je teruggaat naar een oudere revisie van een recordset. - In een echte wereldimplementatie zou je willen weten welke gebruiker de gegevens heeft gewijzigd. Om die revisie veilig te hebben, mag een gebruikersrecord nooit uit de gebruikerstabel worden verwijderd. U moet het account gewoon uitschakelen met een vlag.
- Meestal omvat een enkele gebruikersactie meer dan één tabel. In een echte wereldimplementatie zou u ook moeten bijhouden welke wijzigingen in meerdere tabellen bij een enkele gebruikerstransactie horen en ook in welke volgorde. In een echt gebruiksgeval zou u alle wijzigingen van een enkele transactie samen willen terugdraaien, in omgekeerde volgorde. Dat zou een extra revisietabel vereisen die de gebruikers en transacties bijhoudt en een losse relatie heeft met al die individuele revisies in de geschiedenistabellen.
Voordelen:
- volledig in database, onafhankelijk van applicatiecode. (nou ja, niet wanneer het bijhouden van gebruikerstransacties belangrijk is. dat zou enige logica vereisen buiten het bereik van de enkele zoekopdracht)
- alle gegevens hebben de oorspronkelijke indeling, geen impliciete typeconversies.
- goede zoekprestaties in de revisies
- gemakkelijk terugdraaien. Doe gewoon een simpele
INSERT .. ON DUPLICATE KEY UPDATE ..
verklaring op de originele tabel, met behulp van de gegevens van de revisie die u wilt terugdraaien.
Verdiensten:
- Moeilijk handmatig te implementeren.
- Moeilijk (maar niet onmogelijk) te automatiseren als het gaat om databasemigraties / applicatie-updates.
Zoals hierboven al vermeld, doctrines versionable
doet iets soortgelijks.
Optie B:een centrale tabel met wijzigingslogboeken hebben
voorwoord:slechte praktijk, alleen weergegeven ter illustratie van het alternatief.
Deze benadering is sterk afhankelijk van toepassingslogica, die verborgen zou moeten zijn in een gegevenslaag/model.
Je hebt een centrale geschiedenistabel die bijhoudt op
- Wie deed het
- wanneer
- wijzigen, invoegen of verwijderen
- welke gegevens
- in welk veld
- van welke tafel
Net als bij de andere benadering, wilt u misschien ook bijhouden welke individuele gegevenswijzigingen bij een enkele gebruikersactie / transactie horen en in welke volgorde.
Voordelen:
- het is niet nodig om synchroon te blijven met de originele tabel bij het toevoegen van velden aan een tabel of het maken van een nieuwe tabel. het schaalt transparant.
Verdiensten:
- slecht gebruik van een eenvoudige waarde =sleutelarchief in database
- slechte zoekprestaties vanwege impliciete typeconversies
- kan de algehele prestaties van de applicatie/database vertragen, wanneer de centrale geschiedenistabel een knelpunt wordt vanwege schrijfvergrendelingen (dit geldt alleen voor specifieke engines met tabelvergrendelingen, d.w.z. MyISAM)
- Het is veel moeilijker om terugdraaiingen te implementeren
- mogelijke gegevensconversiefouten/precisieverlies door impliciete typeconversie
- houdt geen wijzigingen bij wanneer u rechtstreeks toegang heeft tot de database ergens in uw code in plaats van uw model / gegevenslaag te gebruiken en vergeet dat u in dit geval handmatig naar het revisielogboek moet schrijven. Kan een groot probleem zijn als je in een team met andere programmeurs werkt.
Conclusie:
- Optie B kan erg handig zijn voor kleine apps als een simpele "drop-in" wanneer het alleen voor het loggen van wijzigingen is.
- Als je terug in de tijd wilt gaan en gemakkelijk de verschillen tussen historische revisies 123 wilt vergelijken naar revisie 125 en/of terug naar de oude gegevens, dan Optie A is de moeilijke weg om te gaan.