Fremdschlüsseleinschränkungen: Wann wird ON UPDATE und ON DELETE verwendet?

195

Ich entwerfe mein Datenbankschema mit MySQL Workbench, was ziemlich cool ist, weil Sie Diagramme erstellen können und diese konvertieren: P.

Wie auch immer, ich habe mich für InnoDB entschieden, da es Fremdschlüssel unterstützt. Eine Sache, die mir jedoch aufgefallen ist, ist, dass Sie die Optionen On Update und On Delete für Fremdschlüssel festlegen können. Kann jemand erklären, wo "Restrict", "Cascade" und set null in einem einfachen Beispiel verwendet werden könnten?

Angenommen, ich habe eine userTabelle mit a userID. Angenommen, ich habe eine Nachrichtentabelle, messagedie viele-zu-viele enthält und zwei Fremdschlüssel enthält (die userIDin der userTabelle auf denselben Primärschlüssel verweisen ). Ist es in diesem Fall sinnvoll, die Optionen On Update und On Delete festzulegen? Wenn ja, welches wähle ich? Wenn dies kein gutes Beispiel ist, können Sie sich bitte ein gutes Beispiel einfallen lassen, um zu veranschaulichen, wie nützlich diese sein können.

Vielen Dank

Meltuhamy
quelle

Antworten:

485

Zögern Sie nicht, die Datenbank einzuschränken. Sie werden sicher eine konsistente Datenbank haben, und das ist einer der guten Gründe, eine Datenbank zu verwenden. Insbesondere, wenn mehrere Anwendungen dies anfordern (oder nur eine Anwendung, jedoch mit einem Direktmodus und einem Stapelmodus unter Verwendung verschiedener Quellen).

Mit MySQL haben Sie keine erweiterten Einschränkungen wie in postgreSQL, aber zumindest die Fremdschlüsseleinschränkungen sind ziemlich erweitert.

Wir nehmen ein Beispiel, eine Firmentabelle mit einer Benutzertabelle, die Personen aus dieser Firma enthält

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

Schauen wir uns die ON UPDATE- Klausel an:

  • ON UPDATE RESTRICT : Die Standardeinstellung : Wenn Sie versuchen, eine company_id in der Tabelle COMPANY zu aktualisieren, lehnt die Engine den Vorgang ab, wenn mindestens ein USER eine Verknüpfung zu dieser Firma herstellt.
  • ON UPDATE KEINE AKTION : wie RESTRICT.
  • ON UPDATE CASCADE : das Beste normalerweise : Wenn Sie eine company_id in einer Zeile der Tabelle COMPANY aktualisieren, aktualisiert die Engine sie entsprechend in allen USER-Zeilen, die auf dieses UNTERNEHMEN verweisen (jedoch keine Trigger in der USER-Tabelle aktiviert, Warnung). Der Motor wird die Änderungen für Sie verfolgen, es ist gut.
  • ON UPDATE SET NULL : Wenn Sie eine company_id in einer Zeile der Tabelle COMPANY aktualisieren, setzt die Engine die zugehörigen USERs company_id auf NULL (sollte im Feld USER company_id verfügbar sein). Ich kann bei einem Update nichts Interessantes sehen, aber ich kann mich irren.

Und jetzt auf ON DELETE Seite:

  • ON DELETE RESTRICT : Die Standardeinstellung : Wenn Sie versuchen, eine company_id-ID in der Tabelle COMPANY zu löschen, lehnt die Engine den Vorgang ab, wenn ein USER mindestens Links zu dieser Firma erstellt, was Ihr Leben retten kann.
  • ON LÖSCHEN KEINE AKTION : wie RESTRICT
  • ON DELETE CASCADE : gefährlich : Wenn Sie eine Unternehmenszeile in der Tabelle COMPANY löschen, löscht die Engine auch die zugehörigen USERs. Dies ist gefährlich, kann jedoch verwendet werden, um automatische Bereinigungen für sekundäre Tabellen durchzuführen (es kann also etwas sein, das Sie möchten, aber mit Sicherheit nicht für ein Beispiel eines UNTERNEHMENS <-> USER).
  • ON DELETE SET NULL : Handvoll : Wenn Sie eine COMPANY-Zeile löschen, haben die zugehörigen USER automatisch die Beziehung zu NULL. Wenn Null Ihr Wert für Benutzer ohne Unternehmen ist, kann dies ein gutes Verhalten sein. Möglicherweise müssen Sie die Benutzer in Ihrer Anwendung als Autoren einiger Inhalte behalten, aber das Entfernen des Unternehmens ist für Sie kein Problem.

Normalerweise ist meine Standardeinstellung: ON DELETE RESTRICT ON UPDATE CASCADE . mit einigen ON DELETE CASCADEfür Track-Tabellen (Protokolle - nicht alle Protokolle -, solche Dinge) undON DELETE SET NULL wenn die Master-Tabelle ein 'einfaches Attribut' für die Tabelle ist, die den Fremdschlüssel enthält, wie eine JOB-Tabelle für die USER-Tabelle.

Bearbeiten

Es ist lange her, seit ich das geschrieben habe. Jetzt denke ich, ich sollte eine wichtige Warnung hinzufügen. MySQL hat eine große dokumentierte Einschränkung mit Kaskaden. Kaskaden lösen keine Auslöser aus . Wenn Sie also zu sicher genug sind, Trigger zu verwenden, sollten Sie Kaskadenbeschränkungen vermeiden.

MySQL-Trigger werden nur für Änderungen aktiviert, die von SQL-Anweisungen an Tabellen vorgenommen wurden. Sie werden weder für Änderungen in Ansichten noch für Änderungen an Tabellen aktiviert, die von APIs vorgenommen wurden, die keine SQL-Anweisungen an den MySQL Server übertragen

==> Siehe unten, die letzte Änderung, die Dinge bewegen sich auf dieser Domain

Trigger werden nicht durch Fremdschlüsselaktionen aktiviert.

Und ich glaube nicht, dass dies eines Tages behoben wird. Fremdschlüsseleinschränkungen werden vom InnoDb-Speicher und Trigger von der MySQL SQL-Engine verwaltet. Beide sind getrennt. Innodb ist der einzige Speicher mit Constraint-Management. Vielleicht werden eines Tages Trigger direkt in die Speicher-Engine eingefügt, vielleicht auch nicht.

Ich bin jedoch der Meinung, welches Element Sie zwischen der schlechten Triggerimplementierung und der sehr nützlichen Unterstützung für Fremdschlüsseleinschränkungen wählen sollten. Und sobald Sie sich an die Datenbankkonsistenz gewöhnt haben, werden Sie PostgreSQL lieben.

12/2017-Aktualisierung dieser Bearbeitung über MySQL:

Wie von @IstiaqueAhmed in den Kommentaren angegeben, hat sich die Situation in diesem Bereich geändert. Folgen Sie also dem Link und überprüfen Sie die aktuelle Situation (die sich in Zukunft möglicherweise wieder ändern wird).

Regilero
quelle
8
ON DELETE CASCADE : dangerous- Mit einer Prise Salz einnehmen.
Tag, wenn
3
Sie müssen auf Kaskadierung achten, da dies Ihr System blockieren kann, wenn viele Datensätze geändert werden müssen. Das Löschen von Kaskaden sollte vor der Verwendung besonders sorgfältig geprüft werden. Oft möchten Sie wirklich, dass das Löschen nicht erfolgt, wenn untergeordnete Datensätze vorhanden sind. Ich möchte nicht, dass ein Kunde löscht, um die Finanzdaten für die Oreders zu löschen, die er zuvor hatte. Manchmal ist es besser, sicherzustellen, dass das Cacading nicht aktiviert ist, und eine Möglichkeit bereitzustellen, Datensätze als inaktiv zu amrken.
HLGEM
1
In Bezug auf die Business - Logik gibt es einen Fall, die interessant sein könnte SET NULLin einem ON UPDATE: ein Unternehmen , das Aktualisieren einer Ablösung der Gesellschaft repräsentiert> Benutzer Beziehung. Beispiel: Wenn ein Unternehmen seinen Geschäftstyp ändert, sind die vorherigen Benutzer möglicherweise nicht mehr mit diesem Geschäft verbunden. Daher NULList dies für diesen Index möglicherweise vorzuziehen.
CPHPython
1
@regilero, anscheinend hat sich der Inhalt Ihres ersten Links ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) zur MySQL-Site geändert. Es heißt This includes changes to base tables that underlie updatable viewsanstelle dessen, was Sie eingefügt haben, dhThey do not activate for changes in views
Istiaque Ahmed
6
"Ich möchte nicht, dass ein Kunde die Finanzdaten für die Bestellungen, die er zuvor hatte, löscht." In einer solchen Situation benötigen Sie wahrscheinlich trotzdem die Kundendaten. Ihr Design sollte den Kunden wahrscheinlich als inaktiv markieren und seine Zeile nicht aus der Datenbank löschen. In der Praxis war es meine Berufserfahrung, dass Sie eigentlich sehr selten etwas löschen möchten und es vorziehen, standardmäßig inaktiv zu markieren. In den Fällen, in denen das dauerhafte Löschen in Ordnung ist , CASCADE DELETEist dies im Allgemeinen auch in Ordnung, sogar bevorzugt. Ich halte es nicht für besonders gefährlich.
GrandOpener
3

Ergänzung zur @ MarkR-Antwort - Eine Sache, die zu beachten ist, ist, dass viele PHP-Frameworks mit ORMs das erweiterte DB-Setup (Fremdschlüssel, kaskadierendes Löschen, eindeutige Einschränkungen) nicht erkennen oder verwenden. Dies kann zu unerwartetem Verhalten führen.

Wenn Sie beispielsweise einen Datensatz mit ORM DELETE CASCADElöschen und Datensätze in verwandten Tabellen löschen, führt der Versuch von ORM, diese verwandten Datensätze (häufig automatisch) zu löschen, zu einem Fehler.

lxa
quelle
11
Das wäre ein Grund, dieses spezielle ORM nicht zu verwenden. Jedes Tool, das bei der Datenbankunterstützung so schlecht ist, ist nicht vertrauenswürdig. Fremdschlüssel und kaskadierende Löschungen oder Aktualisierungen sind DB-Grundlagen, keine fortgeschrittenen Konzepte, und es sollte niemals eine Realtional-Datenbank ohne Fremdschlüsseleinschränkungen entworfen werden!
HLGEM
Das Problem ist, dass sie Fehler werfen. Ist es möglich, DELETES ZU BESCHRÄNKEN, aber hat die Engine keine Fehler generiert und trotzdem die Semantik beibehalten? Ich möchte, dass mein Programm weiterhin läuft und gleichzeitig andere Daten vor dem Löschen schützt.
TheRealChx101
2

Sie müssen dies im Kontext der Anwendung berücksichtigen. Im Allgemeinen sollten Sie eine Anwendung entwerfen, keine Datenbank (die Datenbank ist einfach Teil der Anwendung).

Überlegen Sie, wie Ihre Anwendung auf verschiedene Fälle reagieren soll.

Die Standardaktion besteht darin, den Vorgang einzuschränken (dh nicht zuzulassen), was normalerweise gewünscht wird, da er dumme Programmierfehler verhindert. Bei DELETE kann CASCADE jedoch auch nützlich sein. Es hängt wirklich von Ihrer Anwendung ab und davon, wie Sie bestimmte Objekte löschen möchten.

Persönlich würde ich InnoDB verwenden, weil es Ihre Daten nicht in den Papierkorb wirft (siehe MyISAM), anstatt weil es FK-Einschränkungen gibt.

MarkR
quelle