Löschen Sie Zeilen mit Fremdschlüssel in PostgreSQL

77

Ich möchte Zeilen löschen, die einen Fremdschlüssel enthalten, aber wenn ich so etwas versuche:

DELETE FROM osoby WHERE id_osoby='1'

Ich bekomme diese Aussage:

FEHLER: Aktualisieren oder Löschen in der Tabelle "osoby" verletzt die Fremdschlüsseleinschränkung "kontakty_ibfk_1" in der Tabelle "kontakty". DETAIL: Der Schlüssel (id_osoby) = (1) wird weiterhin aus der Tabelle "kontakty" referenziert.

Wie kann ich diese Zeilen löschen?

Michal Loksik
quelle
3
Überprüfen Sie dies auch bei der Löschkaskade ;) Es ist jedoch gut, diese Einstellungen in Ihrer Tabelle zu haben. Wenn creating foreign keyswir add parent then child. Also beim Löschen wir delete child and then parent;)
bonCodigo

Antworten:

90

Um dies zu automatisieren, können Sie die Fremdschlüsseleinschränkung mit definieren ON DELETE CASCADE.
Ich zitiere das Handbuch der Fremdschlüsseleinschränkungen :

CASCADE Gibt an, dass beim Löschen einer referenzierten Zeile auch die referenzierenden Zeilen automatisch gelöscht werden sollen.

Schlagen Sie die aktuelle FK-Definition folgendermaßen nach:

SELECT pg_get_constraintdef(oid) AS constraint_def
FROM   pg_constraint
WHERE  conrelid = 'public.kontakty'::regclass  -- assuming pubic schema
AND    conname = 'kontakty_ibfk_1';

Fügen Sie dann das ON DELETE ...Teil ON DELETE CASCADEin einer Anweisung wie folgt hinzu oder ändern Sie es (wobei alles andere unverändert bleibt):

ALTER TABLE kontakty
   DROP CONSTRAINT kontakty_ibfk_1
 , ADD  CONSTRAINT kontakty_ibfk_1
   FOREIGN KEY (id_osoby) REFERENCES osoby (id_osoby) ON DELETE CASCADE;

Da es keine ALTER CONSTRAINTSyntax gibt, löschen Sie die Einschränkung und erstellen Sie sie in einer einzelnen ALTER TABLEAnweisung neu. Dies vermeidet mögliche Rennbedingungen bei gleichzeitigem Schreibzugriff.

Dazu benötigen Sie natürlich die Berechtigungen. Die Operation nimmt eine ACCESS EXCLUSIVESperre für den Tisch kontaktyund eine SHARE ROW EXCLUSIVESperre für den Tisch osoby.

Wenn Sie ALTERdie Tabelle nicht können, können Sie sie nur noch einmal (einmal) oder per Trigger BEFORE DELETE(jedes Mal) löschen .

Erwin Brandstetter
quelle
4
Wir haben keine Tabellen mit ON erstellt DELETE CASCADE. Ich kann die Struktur von Tabellen nicht ändern. Gibt es eine Möglichkeit, Fremdschlüssel automatisch zu löschen? oder wir müssen dem von @juergen d
वरुण
2
@Varun: Wenn Sie ALTERdie Tabelle nicht zum Hinzufügen eines FK verwenden können ON DELETE CASCADE, sind das Löschen per Hand (einmal) oder per Trigger BEFORE DELETE(jedes Mal) die verbleibenden Optionen.
Erwin Brandstetter
Cool, also lautet die Antwort ON DELETE CASCADE, der Spalte mit dem Fremdschlüssel in Ihrer create-Anweisung etwas hinzuzufügen .
425nesp
1
@ 425nesp: Ich habe explizite Anweisungen hinzugefügt.
Erwin Brandstetter
In meinem Fall brauchte ich DDL, um die genaue Namensbeschränkung zu sehen. In diesem Fall wäre es\d kontakty
marquicus
36

Sie können einen Fremdschlüssel nicht löschen, wenn er noch auf eine andere Tabelle verweist. Löschen Sie zuerst die Referenz

delete from kontakty
where id_osoby = 1;

DELETE FROM osoby 
WHERE id_osoby = 1;
Jürgen d
quelle
28

Dies sollte nicht als allgemeine Lösung empfohlen werden. Für das einmalige Löschen von Zeilen in einer Datenbank, die sich nicht in der Produktion befindet oder nicht aktiv verwendet wird, können Sie möglicherweise die Trigger für die betreffenden Tabellen vorübergehend deaktivieren.

In meinem Fall bin ich im Entwicklungsmodus und habe einige Tabellen, die über Fremdschlüssel aufeinander verweisen. Das Löschen des Inhalts ist daher nicht ganz so einfach wie das Entfernen aller Zeilen aus einer Tabelle vor der anderen. Für mich hat es also gut funktioniert, den Inhalt wie folgt zu löschen:

ALTER TABLE table1 DISABLE TRIGGER ALL;
ALTER TABLE table2 DISABLE TRIGGER ALL;
DELETE FROM table1;
DELETE FROM table2;
ALTER TABLE table1 ENABLE TRIGGER ALL;
ALTER TABLE table2 ENABLE TRIGGER ALL;

Sie sollten in der Lage sein, WHERE-Klauseln wie gewünscht hinzuzufügen, natürlich mit Vorsicht, um die Integrität der Datenbank nicht zu beeinträchtigen.

Es gibt einige gute, verwandte Diskussionen unter http://www.openscope.net/2012/08/23/subverting-foreign-key-constraints-in-postgres-or-mysql/

Andrew Basile
quelle
17

Es ist eine Weile her, seit diese Frage gestellt wurde, Hoffnung kann helfen. Da Sie die Datenbankstruktur nicht ändern können, können Sie dies tun. gemäß den postgresql docs .

TRUNCATE - Leert eine Tabelle oder einen Satz von Tabellen.

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

Beschreibung

TRUNCATE entfernt schnell alle Zeilen aus einer Reihe von Tabellen. Es hat den gleichen Effekt wie ein nicht qualifiziertes LÖSCHEN für jede Tabelle, aber da es die Tabellen nicht tatsächlich scannt, ist es schneller. Darüber hinaus wird sofort Speicherplatz zurückgewonnen, anstatt eine nachfolgende VACUUM-Operation zu erfordern. Dies ist am nützlichsten bei großen Tabellen.


Schneiden Sie die Tabelle othertable ab und kaskadieren Sie über Fremdschlüsseleinschränkungen zu allen Tabellen, die auf othertable verweisen:

TRUNCATE othertable CASCADE;

Das gleiche und auch alle zugehörigen Sequenzgeneratoren zurücksetzen:

TRUNCATE bigtable, fattable RESTART IDENTITY;

Schneiden Sie alle zugehörigen Sequenzgeneratoren ab und setzen Sie sie zurück:

TRUNCATE revinfo RESTART IDENTITY CASCADE ;
ABl
quelle
Schön, sehr nützlich.
Setzwelle
5

Dies bedeutet, dass Sie in der Tabelle kontaktyeine Zeile haben, die auf die Zeile verweist, in der osobySie löschen möchten. Sie müssen diese Zeile zuerst löschen oder eine Kaskadenlöschung für die Beziehung zwischen Tabellen festlegen.

Powodzenia!

Zibi
quelle