Wie sollen Löschungen in der Datenbank behandelt werden?


44

Ich möchte eine Funktion zum "Wiederherstellen" in einer Webanwendung implementieren, damit ein Benutzer seine Meinung ändern und einen gelöschten Datensatz wiederherstellen kann. Überlegungen zur Umsetzung? Einige Optionen, die ich in Betracht gezogen habe, sind das Löschen des betreffenden Datensatzes und das Speichern der Änderungen in einer separaten Prüftabelle oder das Nicht-Löschen des Datensatzes und die Verwendung einer booleschen Spalte "gelöscht", um ihn als gelöscht zu markieren. Die letztere Lösung würde zusätzliche Anwendungslogik erfordern, um die "gelöschten" Datensätze unter normalen Umständen zu ignorieren, würde es jedoch wesentlich einfacher machen, die Datensätze auf der Anwendungsseite wiederherzustellen.


Ich habe vergessen zu erwähnen, dass im zweiten Fall die markierten Datensätze nach einer angemessenen Zeit gelöscht oder verschoben werden müssten.
Abie

Welche Datenbank verwenden Sie?
Evan Carroll

Temporale Tabelle ist die beste Lösung für SQL Server 2016 und höher.
Sameer

Antworten:


37

Ja, ich würde definitiv die zweite Option wählen, aber ich würde ein weiteres Feld als Datumsfeld hinzufügen.

Also fügst du hinzu:

delete       boolean
delete_date  timestamp

Damit können Sie Zeit für das Wiederherstellen der Aktion geben.

Wenn die Zeit weniger als eine Stunde beträgt, kann die Löschung rückgängig gemacht werden.

Um den gelöschten Eintrag wirklich zu löschen, erstellen Sie einfach eine gespeicherte Prozedur, die jeden Eintrag bereinigt, wobei "delete" auf "true" gesetzt und die Zeit länger als eine Stunde ist, und legen Sie sie als Cron-Tab ab, der alle 24 Stunden ausgeführt wird

Die Stunde ist nur ein Beispiel.


Alternativ können Sie ein anderes Flag ( cleanedoder etwas anderes) haben , das angibt, dass die mit diesem Datensatz verknüpften Daten ordnungsgemäß und umfassend gelöscht wurden. Der Datensatz kann wiederhergestellt werden, sofern dies nicht cleanedder Fall ist. In diesem Fall kann er nicht wiederhergestellt werden.
Gaurav

14
Dies ist der übliche Ansatz. Normalerweise verwende ich ein Feld deleted_at, das sowohl die Semantik des deleteBooleschen als auch den delete_dateZeitstempel enthält. Wenn deleted_atheißt NULLder Fall handhaben deleteist FALSEund delete_dateist NULL, deleted_ateinen Zeitstempel Griff der Fall enthalten deleteist TRUEund delete_dateenthält einen Zeitstempel, können Sie eine Zeitersparnis, Speicher- und Anwendungslogik.
Julien

1
Ich mag das Boolesche und Datumsfeld. Abhängig davon, wie Sie die Löschlogik implementieren, können Sie sogar eine separate Tabelle haben, die das Datum und den eindeutigen Schlüssel für den Datensatz enthält, der "gelöscht" wurde. Gespeicherte Prozeduren machen dies einfach. Der zusätzliche Platz pro Zeile wird auf 1 Bit im Vergleich zu 8+ verringert. Sie können auch über Löschungen pro Tag berichten, ohne die Quelltabelle zu berühren.
AndrewSQL

Hinweis: delete ist in MySQL ein reserviertes Wort.
Jason Rikard

Denken Sie daran, dass ein gefilterter Index für Ihr deletedFeld die Leistung erheblich verbessern kann, wenn Sie nach nicht gelöschten Zeilen suchen
Ross Presser,

21

In unseren Anwendungen haben wir nicht wirklich löscht etwas an einem Benutzer fordert ohnehin (unsere Kunden in regulierten Umgebungen, wo etwas zu löschen möglicherweise zu rechtlichen Problemen führen kann).

Wir bewahren die älteren Versionen in einer separaten Prüftabelle auf (also für die Tabelle some_table, in der es auch eine Tabelle namens some_table_audit gibt), die abgesehen von einer zusätzlichen Versionskennung (einem Zeitstempel, wenn Ihre Datenbank Zeitwerte unterstützt, die granular genug sind, eine ganzzahlige Versionsnummer) identisch ist oder UUID, die ein Fremdschlüssel für eine allgemeine Prüftabelle ist (oder so weiter), und die Prüftabelle automatisch per Trigger aktualisieren (damit nicht der gesamte Code, der die Datensätze aktualisiert, auf die Prüfungsanforderung aufmerksam gemacht werden muss).

Diesen Weg:

  • Der Löschvorgang ist nur ein einfaches Löschen. Dazu muss kein zusätzlicher Code hinzugefügt werden. Möglicherweise möchten Sie jedoch aufzeichnen, wer welche zu löschenden Zeilen angefordert hat, auch wenn diese nicht tatsächlich gelöscht wurden.
  • Einfügungen und Aktualisierungen sind ähnlich einfach
  • Sie können das Wiederherstellen oder Zurücksetzen implementieren, indem Sie einfach die "normale" Zeile auf eine alte Version zurücksetzen (der Audit-Trigger wird erneut ausgelöst, sodass die Audit-Trail-Tabelle auch diese Änderung widerspiegelt).
  • Sie können die Möglichkeit bieten, eine frühere Version zu überprüfen oder auf diese zurückzugreifen und nicht nur die letzte wiederherzustellen
  • Sie müssen nicht hinzufügen "wird als gelöscht markiert?" Überprüft jeden Codepunkt, der sich auf die betreffende Tabelle bezieht, oder aktualisiert die Überwachungskopie-Logik auf jeden Codepunkt, der Zeilen löscht / aktualisiert (obwohl Sie entscheiden müssen, was mit gelöschten Zeilen in der Überwachungstabelle zu tun ist: Wir haben eine gelöscht / nicht kennzeichnen für jede Version dort, so dass es keine Lücke im Verlauf gibt, wenn Datensätze gelöscht und später nicht gelöscht werden)
  • Wenn Sie die Prüfkopien in einer separaten Tabelle aufbewahren, können Sie sie problemlos in verschiedene Dateigruppen aufteilen.

Wenn Sie anstelle (oder auch) einer ganzzahligen Versionsnummer einen Zeitstempel verwenden, können Sie damit die älteren Kopien nach einer festgelegten Zeit löschen, falls erforderlich. Aber Speicherplatz ist heutzutage relativ billig. Wenn wir keinen Grund haben, alte Daten zu löschen (dh Datenschutzbestimmungen, die besagen, dass Sie Kundendaten nach X Monaten / Jahren löschen sollten), würden wir dies nicht tun.


Diese Antwort gibt es schon seit ein paar Jahren, und seitdem haben sich einige wichtige Dinge, die diese Art der Planung beeinflussen könnten, geändert. Ich werde nicht ins Detail gehen, aber kurz, zum Wohle der Leute, die dies heute lesen:

  • In SQL Server 2016 wurden "systemversionierte temporale Tabellen" eingeführt, die einen Großteil dieser Arbeit für Sie erledigen, und außerdem wird ein netter syntaktischer Zucker bereitgestellt, um die Erstellung und Pflege historischer Abfragen zu vereinfachen, und sie koordinieren eine Untergruppe von Schemaänderungen zwischen den Tabellen Basis- und Verlaufstabellen. Sie sind nicht ohne Vorbehalte, aber sie sind ein mächtiges Werkzeug für diese Art von Zweck. Ähnliche Funktionen sind auch in anderen DB-Systemen verfügbar.

  • Änderungen der Datenschutzgesetze, insbesondere die Einführung der DSGVO, können die Frage, wann Daten endgültig gelöscht werden sollten, erheblich verändern. Sie müssen abwägen, inwieweit Daten, die für Prüfungszwecke zu einem späteren Zeitpunkt nützlich (oder sogar gesetzlich vorgeschrieben) sind, nicht gelöscht werden, um die (in den einschlägigen Gesetzen allgemein und spezifisch festgelegten) Rechte der Menschen zu respektieren Ihre Entwürfe. Dies kann ein Problem bei vom System versionierten Zeittabellen sein, da Sie den Verlauf nicht ändern können, um persönliche Daten zu bereinigen, ohne kurzfristige Schemaänderungen vorzunehmen, um die Verlaufsverfolgung zu deaktivieren, während Sie Änderungen vornehmen.


Wie gehen Sie mit dem Löschen und Umbenennen von Spalten um? Alles auf null setzen?
Stijn

1
@Stijn: Es kommt nicht oft vor, dass die Strukturen geändert werden, so dass nicht viel passiert. Colunms werden in der Regel nie entfernt, wenn sie einmal in der Produktion vorhanden sind. Wenn sie nicht mehr verwendet werden, löschen Sie einfach alle Einschränkungen, die sie von NULL abhalten würden. und hören Sie auf, auf sie in anderem Code zu verweisen. Umbenennen: Fügen Sie neue hinzu, verwenden Sie keine alten mehr und kopieren Sie bei Bedarf Daten von alten zu neuen. Wenn Sie Spalten umbenennen, stellen Sie sicher, dass die gleiche Änderung gleichzeitig an der Basis- und der Überwachungstabelle vorgenommen wird.
David Spillett

9

Bei einer booleschen gelöschten Spalte treten Probleme auf, wenn Ihre Tabelle wächst und sehr groß wird. Ich schlage vor, dass Sie gelöschte Spalten einmal pro Woche (je nach Spezifikation mehr oder weniger) in eine andere Tabelle verschieben. Auf diese Weise haben Sie einen schönen kleinen aktiven Tisch und einen großen Tisch mit allen Aufzeichnungen, die im Laufe der Zeit zusammengetragen wurden.


7

Ich würde mit dem separaten Tisch gehen. Ruby on Rails hat ein acts_as_versionedPlugin, das im Grunde genommen eine Zeile mit dem Postfix in einer anderen Tabelle speichert, _versionbevor es aktualisiert wird. Während Sie dieses genaue Verhalten nicht benötigen, sollte es auch für Ihren Fall funktionieren (vor dem Löschen kopieren).

Wie bei @Spredzy würde ich auch empfehlen, eine delete_dateSpalte hinzuzufügen , um programmgesteuert Datensätze zu löschen, die nach X Stunden / Tagen / was auch immer nicht wiederhergestellt wurden.


4

Die Lösung, die wir intern für diese Angelegenheit verwenden, besteht darin, eine Statusspalte mit einigen fest codierten Werten für einige bestimmte Zustände des Objekts zu haben: Gelöscht, Aktiv, Inaktiv, Offen, Geschlossen, Gesperrt - jeder Status hat eine Bedeutung, die in der Anwendung verwendet wird. Aus Sicht von db entfernen wir keine Objekte, wir ändern nur den Status und behalten den Verlauf für jede Änderung in der Objekttabelle bei.


3

Wenn Sie sagen, dass "die letztere Lösung zusätzliche Anwendungslogik zum Ignorieren der 'gelöschten' Datensätze erfordern würde", besteht die einfache Lösung darin, eine Ansicht zu haben, die sie herausfiltert.


Es ist nicht nur eine Frage der Aussicht. Alle Operationen, die am Set ausgeführt werden, müssten die "gelöschten" Datensätze ausschließen.
Abie

2

Ähnlich wie von Spredzy vorgeschlagen, verwenden wir in all unseren Anwendungen ein Zeitstempelfeld zum Löschen. Der Boolesche Wert ist überflüssig, da der eingestellte Zeitstempel anzeigt, dass der Datensatz gelöscht wurde. Auf diese Weise ergänzt unser PDO AND (deleted IS NULL OR deleted = 0)die select-Anweisungen immer, es sei denn, das Modell fordert ausdrücklich die Einbeziehung gelöschter Datensätze an.

Wir sammeln derzeit keine Daten mit Ausnahme von Tabellen, die Blobs oder Texte enthalten. Der Abstand ist trivial, wenn die Datensätze gut normalisiert sind, und die Indizierung des deletedFelds wirkt sich nur begrenzt auf die Auswahlgeschwindigkeit aus.


0

Alternativ können Sie den Benutzern (und Entwicklern) die Verantwortung überlassen und eine Abfolge von "Sind Sie sicher?", "Sind Sie sich sicher?" und "Bist du dir absolut sicher?" Fragen, bevor der Datensatz gelöscht wird. Mild witzig, aber eine Überlegung wert.


0

Ich bin es gewohnt, Tabellenzeilen mit Spalten wie "DeletedDate" zu sehen, und ich mag sie nicht. Der Begriff "gelöscht" bedeutet, dass die Eingabe nicht an erster Stelle hätte erfolgen dürfen. Sie können praktisch nicht aus der Datenbank entfernt werden, aber ich möchte nicht, dass sie mit meinen aktuellen Daten in Kontakt kommen. Logisch gelöschte Zeilen sind per Definition kalte Daten, es sei denn, jemand möchte ausdrücklich gelöschte Daten sehen.

Darüber hinaus muss jede geschriebene Abfrage sie ausdrücklich ausschließen und Indizes müssen sie ebenfalls berücksichtigen.

Was ich sehen möchte, ist eine Änderung auf der Ebene der Datenbankarchitektur und der Anwendungsebene: Erstellen Sie ein Schema mit dem Namen "gelöscht". Jede benutzerdefinierte Tabelle hat im Schema "Gelöscht" ein identisches Äquivalent mit einem zusätzlichen Feld, das Metadaten enthält - den Benutzer, der sie wann gelöscht hat. Fremdschlüssel müssen erstellt werden.

Als nächstes werden Löschvorgänge zu Einfügelöschvorgängen. Zuerst wird die zu löschende Zeile in das entsprechende Schema eingefügt. Die betreffende Zeile in der Haupttabelle kann dann gelöscht werden. Zusätzliche Logik muss jedoch irgendwo entlang der Linie hinzugefügt werden. Fremdschlüsselverletzungen können behandelt werden.

Fremdschlüssel müssen ordnungsgemäß behandelt werden. Es ist keine gute Praxis, eine Zeile logisch löschen zu lassen, deren primäre / eindeutige Spalte jedoch Spalten in anderen Tabellen enthält, die darauf verweisen. Das sollte sowieso nicht passieren. Ein regulärer Job kann Witwenzeilen entfernen (Zeilen, deren Primärschlüssel trotz eines Fremdschlüssels keine Referenzen in anderen Tabellen enthalten). Dies ist jedoch eine Geschäftslogik.

Der Gesamtnutzen ist die Reduzierung der Metadaten in der Tabelle und die damit verbundene Leistungsverbesserung. Die Spalte 'deletedDate' besagt, dass diese Zeile eigentlich nicht hier sein sollte. Der Einfachheit halber lassen wir sie dort und überlassen sie der SQL-Abfrage. Wenn eine Kopie der gelöschten Zeile in einem "gelöschten" Schema gespeichert wird, enthält die Haupttabelle mit den aktuellen Daten einen höheren Prozentsatz aktueller Daten (sofern diese rechtzeitig archiviert werden) und weniger unnötige Metadatenspalten. Indizes und Abfragen müssen dieses Feld nicht mehr berücksichtigen. Je kürzer die Zeilengröße, desto mehr Zeilen können auf eine Seite gepasst werden, desto schneller kann SQL Server arbeiten.

Der Hauptnachteil ist die Größe der Operation. Es gibt jetzt zwei Operationen anstelle von einer sowie die zusätzliche Logik und Fehlerbehandlung. Dies kann zu mehr Sperren führen, als das Aktualisieren einer einzelnen Spalte andernfalls erfordern würde. Die Transaktion hält die Tabelle länger gesperrt, und es handelt sich um zwei Tabellen. Das Löschen von Produktionsdaten wird, zumindest nach meiner Erfahrung, nur selten durchgeführt. Trotzdem haben 7,5% von fast 100 Millionen Einträgen in einer der Haupttabellen einen Eintrag in der Spalte "DeletedDate".

Zur Beantwortung der Frage müsste die Anwendung die Funktion "Wiederherstellen" kennen. Dies müsste einfach in umgekehrter Reihenfolge geschehen: Fügen Sie die Zeile aus dem 'gelöschten' Schema in die Haupttabelle ein und löschen Sie dann die Zeile aus dem 'gelöschten Schema. Auch hier ist eine zusätzliche Logik- und Fehlerbehandlung erforderlich, um Fehler, Probleme mit Fremdschlüsseln und dergleichen zu vermeiden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.