Um diese Frage richtig zu beantworten, müssen Sie sich zunächst entscheiden: Was bedeutet "Löschen" im Kontext dieses Systems / dieser Anwendung?
Um diese Frage zu beantworten , müssen Sie noch eine weitere Frage beantworten: Warum werden Datensätze gelöscht?
Es gibt eine Reihe von guten Gründen, warum ein Benutzer Daten löschen muss. Normalerweise finde ich, dass es genau einen Grund (pro Tabelle) gibt, warum ein Löschen notwendig sein könnte. Einige Beispiele sind:
- So fordern Sie Speicherplatz zurück
- Harte Löschung gemäß Aufbewahrungs- / Datenschutzrichtlinie erforderlich;
- Beschädigte / hoffnungslos falsche Daten, einfacher zu löschen und neu zu generieren als zu reparieren.
- Die meisten Zeilen werden gelöscht, z. B. eine Protokolltabelle, die auf X Datensätze / Tage begrenzt ist.
Es gibt auch einige sehr schlechte Gründe für ein hartes Löschen (dazu später mehr):
- So korrigieren Sie einen geringfügigen Fehler. Dies unterstreicht normalerweise die Faulheit der Entwickler und eine feindliche Benutzeroberfläche.
- Eine Transaktion "stornieren" (zB Rechnung, die niemals hätte fakturiert werden dürfen).
- Weil du kannst .
Warum, fragen Sie sich, ist das wirklich so eine große Sache? Was ist los mit guten alten DELETE?
- In jedem System, das auch nur remote an Geld gebunden ist, verstößt das Löschen gegen alle möglichen Buchhaltungserwartungen, selbst wenn es in ein Archiv / eine Tombstone-Tabelle verschoben wird. Die richtige Vorgehensweise ist ein rückwirkendes Ereignis .
- Archivtabellen neigen dazu, vom Live-Schema abzuweichen. Wenn Sie nur eine neu hinzugefügte Spalte oder Kaskade vergessen, haben Sie diese Daten dauerhaft verloren.
- Ein hartes Löschen kann eine sehr teure Operation sein, insbesondere bei Kaskaden . Viele Leute wissen nicht, dass das Kaskadieren von mehr als einer Ebene (oder in einigen Fällen , je nach DBMS, jedes Kaskadieren) Operationen auf Rekordebene anstelle von festgelegten Operationen zur Folge hat.
- Wiederholtes, häufiges Löschen beschleunigt den Prozess der Indexfragmentierung.
Soft Delete ist also besser, oder? Nein nicht wirklich:
- Das Einrichten von Kaskaden wird extrem schwierig. Sie haben fast immer das, was dem Kunden als verwaiste Zeilen erscheint.
- Sie können nur eine Löschung verfolgen . Was passiert, wenn die Zeile mehrmals gelöscht und wiederhergestellt wird?
- Die Leseleistung leidet, obwohl dies durch Partitionierung, Ansichten und / oder gefilterte Indizes etwas gemindert werden kann.
- Wie bereits angedeutet, kann es in einigen Szenarien / Gerichtsbarkeiten tatsächlich illegal sein.
Die Wahrheit ist, dass beide Ansätze falsch sind. Löschen ist falsch. Wenn Sie diese Frage tatsächlich stellen, bedeutet dies, dass Sie anstelle der Transaktionen den aktuellen Status modellieren. Dies ist eine schlechte, schlechte Praxis im Datenbankland.
Udi Dahan schrieb darüber in Don't Delete - Just Don't . Es gibt immer irgendeine Art von Aufgabe, Transaktion, Aktivität oder (mein bevorzugter Begriff) Ereignis, das tatsächlich das "Löschen" darstellt. Es ist in Ordnung, wenn Sie anschließend eine Denormalisierung in eine Tabelle mit dem aktuellen Status durchführen möchten, dies jedoch erst, nachdem Sie das Transaktionsmodell festgelegt haben.
In diesem Fall haben Sie "Benutzer". Benutzer sind im Wesentlichen Kunden. Kunden haben eine Geschäftsbeziehung mit Ihnen. Diese Beziehung verschwindet nicht einfach in Luft, weil sie ihren Account gekündigt hat. Was wirklich passiert ist:
- Kunde legt Konto an
- Der Kunde storniert das Konto
- Kunde erneuert Konto
- Der Kunde storniert das Konto
- ...
In jedem Fall handelt es sich um denselben Kunden und möglicherweise um denselben Account (dh bei jeder Account-Verlängerung handelt es sich um einen neuen Servicevertrag). Warum löschen Sie Zeilen? Dies ist sehr einfach zu modellieren:
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
Das ist es. Das ist alles dazu. Sie müssen nie etwas löschen. Das oben Genannte ist ein recht gebräuchliches Design, das ein gutes Maß an Flexibilität bietet, das Sie jedoch ein wenig vereinfachen können. Sie könnten entscheiden, dass Sie die Stufe "Vereinbarung" nicht benötigen und "Konto" einfach zu einer "AccountStatus" -Tabelle wechseln lassen.
Wenn in Ihrer Anwendung häufig eine Liste der aktiven Vereinbarungen / Konten benötigt wird, ist dies eine (geringfügig) knifflige Abfrage, für die die folgenden Ansichten jedoch vorgesehen sind:
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
Und du bist fertig. Jetzt haben Sie etwas mit allen Vorteilen von Soft-Deletes, aber keinen der Nachteile:
- Verwaiste Datensätze sind kein Problem, da alle Datensätze jederzeit sichtbar sind. Sie können bei Bedarf einfach aus einer anderen Ansicht auswählen.
- "Löschen" ist normalerweise ein unglaublich billiger Vorgang - nur eine Zeile in eine Ereignistabelle einfügen.
- Es gibt nie eine Chance , jede Geschichte zu verlieren, immer , egal wie schlecht Sie vermasseln.
- Sie können ein Konto nach wie vor hart löschen, wenn Sie dies benötigen (z. B. aus Datenschutzgründen), und Sie können sich darauf verlassen, dass die Löschung sauber vonstatten geht und keinen anderen Teil der App / Datenbank beeinträchtigt.
Das einzige noch zu lösende Problem ist das Leistungsproblem. In vielen Fällen stellt sich heraus, dass es aufgrund des Clustered-Index kein Problem darstellt AgreementStatus (AgreementId, EffectiveDate)- dort wird nur sehr wenig nach E / A gesucht. Sollte es dennoch zu Problemen kommen, gibt es Möglichkeiten, diese zu lösen, indem Trigger, indizierte / materialisierte Ansichten, Ereignisse auf Anwendungsebene usw. verwendet werden.
Sorgen Sie sich jedoch nicht zu früh um die Leistung - es ist wichtiger, das richtige Design zu finden. "Richtig" bedeutet in diesem Fall, die Datenbank so zu verwenden, wie sie als Transaktionssystem verwendet werden soll.