2010-04-21 20 views
33

Ich habe eine Tabelle mit ca. 5 Millionen Zeilen, die eine fk-Einschränkung auf den Primärschlüssel einer anderen Tabelle (auch ca. 5 Millionen Zeilen) hat.PostgreSQL - Einschränkungen deaktivieren

Ich brauche etwa 75000 Zeilen aus beiden Tabellen zu löschen. Ich weiß, wenn ich versuche, dies mit der fk-Einschränkung zu tun, wird es eine unannehmbare Menge an Zeit benötigen.

Ausgehend von einem Oracle-Hintergrund war mein erster Gedanke, die Einschränkung zu deaktivieren, tun Sie das Löschen & dann wieder aktivieren die Einschränkung. PostGres scheint, um Constraint-Trigger zu deaktivieren, wenn ich ein Super-Benutzer bin (ich bin nicht, aber ich logge mich als der Benutzer ein, der die Objekte besitzt/erstellt), aber das scheint nicht ganz das zu sein, was ich möchte.

Die andere Option besteht darin, die Einschränkung zu löschen und sie dann erneut zu aktivieren. Ich bin besorgt, dass die Wiederherstellung der Einschränkung angesichts der Größe meiner Tabellen ewig dauern wird.

Irgendwelche Gedanken?

bearbeiten: nach Billys Ermutigung habe ich versucht, das Löschen ohne Änderung irgendwelcher Einschränkungen und es dauert mehr als 10 Minuten. Ich habe jedoch festgestellt, dass die Tabelle, aus der ich versuche zu löschen, einen selbstreferentiellen Fremdschlüssel ... dupliziert (& nicht indiziert).

Endgültiges Update - Ich habe den selbstreferenziellen Fremdschlüssel gelöscht, habe ihn gelöscht und wieder hinzugefügt. Billy hat rundum Recht, aber leider kann ich seinen Kommentar nicht als Antwort akzeptieren!

+4

Wenn es so lange dauert, sogar mit 5 Millionen Zeilen, dann ist etwas falsch eingestellt Sie haben. –

+0

Was? Das Löschen oder erneute Aktivieren der Einschränkung? Und ja, es ist durchaus möglich, dass etwas falsch oder in einer weniger als optimierten Weise eingerichtet ist - die Datenbank wurde durch den Winterschlaf ziemlich "gebaut" (ich hatte damit nichts zu tun). – azp74

+10

Das Löschen. FK-Prüfungen von indizierten Tabellen benötigen lineare Zeit und entfernen 75000 + 75000 Zeilen = 150 000 Zeilen. Betrachten Sie einen Worst-Case-Vergleich mit 19 Vergleichen pro FK-Prüfung (Binärsuche, lg (5 Millionen) == 19) und vielleicht 20 Maschinenvergleiche pro Zeilenvergleich, was 57 000 000 Vergleichen entspricht. Wenn man eine konservative Schätzung der durchschnittlichen Maschine in Betracht zieht, die in der Lage ist, eine Milliarde Vergleiche pro Sekunde zu machen, sollte dies immer noch weniger als eine Sekunde CPU-Zeit in Anspruch nehmen. Laden von der Festplatte sollte auch kein großes Problem sein, denn selbst bei 5 Millionen Zeilen sollte die Tabelle in RAM passen. –

Antwort

42

Pro vorherige Kommentare sollte es ein Problem sein. Das heißt, es gibt einen Befehl, der möglicherweise das ist, wonach Sie suchen - er legt die Einschränkungen auf "zurückgestellt" fest, sodass sie bei COMMIT und nicht bei jedem Löschen überprüft werden. Wenn Sie nur eine große DELETE aller Zeilen machen, wird es keinen Unterschied machen, aber wenn Sie es in Stücken tun, wird es.

SET CONSTRAINTS ALL DEFERRED 

ist, was Sie in diesem Fall suchen. Beachten Sie, dass Einschränkungen als DEFERRABLE markiert werden müssen, bevor sie zurückgestellt werden können. Zum Beispiel:

ALTER TABLE table_name 
    ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2) 
    DEFERRABLE INITIALLY IMMEDIATE; 

Die Einschränkung dann in einer Transaktion oder eine Funktion aufgeschoben werden kann, wie folgt:

CREATE OR REPLACE FUNCTION f() RETURNS void AS 
$BODY$ 
BEGIN 
    SET CONSTRAINTS ALL DEFERRED; 

    -- Code that temporarily violates the constraint... 
    -- UPDATE table_name ... 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
+1

Sicherlich einen Versuch wert, aber ich bin nicht davon überzeugt, dass verzögerte Einschränkungen schneller sind. AFAIK verschieben sie einfach die Validierungsarbeit von DELETE-time in COMMIT-time. – intgr

+1

Ich hätte dies gegeben, aber die fk fallen lassen und es wieder in Betrieb genommen. Wie bei intgr frage ich mich, ob es nicht nur die Überprüfung der fk auf Commit-Zeit ändern würde, damit ich mich für das nächste Mal daran erinnern werde. – azp74

+1

Ich habe eine Datenbank gelöscht und sie nach dem Ausführen von 'SET CONSTRAINTS ALL DEFERRED' erneut importiert. Gibt es eine Möglichkeit, diese Einschränkungen nach dem Import wieder zu aktivieren? Es ist eine ziemlich große Datei, daher wäre es ziemlich schwierig, die Tabellenerstellung neu zu ordnen. Ich habe das vorher schon einmal verstanden, indem ich die Daten zweimal importiert habe. – taco

-7

Deaktivieren aller Tabellenbedingungen

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName 

- Aktivieren aller Tabellenbedingungen

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName 
+3

Frage war über Postgresql, die diese Fähigkeit nicht hat (ab v9.4). –

+0

Zustimmen v9.4 hat diese Funktion nicht ERROR: Syntaxfehler bei oder in der Nähe von „NOCHECK“ LINE 1: ALTER TABLE Tablename NOCHECK CONSTRAINT constraintName –

3

(Diese Antwort geht davon aus, dass Sie alle Zeilen dieser Tabellen löschen möchten, nicht nur eine Auswahl.)

Ich musste dies auch tun, aber als Teil einer Testsuite. Ich fand die Antwort, schlug elsewhere on SO vor.Verwenden Sie TRUNCATE TABLE wie folgt:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE]; 

Die folgende löscht schnell alle Zeilen aus Tabellen table1, table2 und table3, vorausgesetzt, dass es keine Hinweise auf Zeilen dieser Tabellen aus den Tabellen nicht aufgeführt:

TRUNCATE TABLE table1, table2, table3; 

Solange Referenzen zwischen den aufgelisteten Tabellen bestehen, löscht PostgreSQL alle Zeilen ohne Rücksicht auf referentielle Integrität. Wenn eine andere als die aufgeführten Tabellen auf eine Zeile einer dieser Tabellen verweist, schlägt die Abfrage fehl.

Sie können jedoch die Abfrage in Frage kommen, so dass sie auch alle Tabellen mit Verweisen auf die aufgeführten Tabellen kürzt (obwohl ich nicht versucht haben):

TRUNCATE TABLE table1, table2, table3 CASCADE; 

Standardmäßig werden die Sequenzen dieser Tabellen nicht Nummerierung neu starten. Neue Zeilen werden mit der nächsten Nummer der Sequenz fortgesetzt. Zum erneuten Starten Sequenznummerierung:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY; 
7

Was für mich gearbeitet wurde deaktivieren eines nach dem anderem der TRIGGERS diesen Tabellen, die würden in dem DELETE Betrieb beteiligt sein.

ALTER TABLE reference DISABLE TRIGGER ALL; 
DELETE FROM reference WHERE refered_id > 1; 
ALTER TABLE reference ENABLE TRIGGER ALL; 

Lösung funktioniert in Version 9.3.16. In meinem Fall ging die Zeit von 45 Minuten bis 14 Sekunden unter Ausführung von DELETE Operationen.

Wie im Kommentarabschnitt von @amphetamachine angegeben, benötigen Sie die Berechtigungen admin für die Tabellen, um diese Aufgabe ausführen zu können.

+1

Beachten Sie, dass der PostgreSQL Benutzer die 'ALTER TABLE' Ausführen von Befehlen muss der Besitzer, dass sein Tabelle. – amphetamachine

0

Wenn Sie DISABLE TRIGGER ALL versuchen und einen Fehler wie permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (ich dies auf Amazon RDS bekam), versuchen Sie dies:

set session_replication_role to replica; 

Wenn dies gelingt, alle Trigger, die Tabellenbedingungen zugrunde liegen, werden deaktiviert. Jetzt liegt es an Ihnen, sicherzustellen, dass Ihre Änderungen die Datenbank in einem konsistenten Zustand belassen!

Dann, wenn Sie fertig sind, löst reaktivieren & Einschränkungen für Ihre Sitzung mit:

set session_replication_role to default;