Löschen Sie alle Daten in der Postgres-Datenbank

14

Ich habe einen neuen Datenbankspeicherauszug von einem Produktionsserver mit den Flags --data-onlyund erstellt --column-inserts, sodass ich nur eine Reihe von Einfügeanweisungen zum Einfügen von Daten habe, wenn eine Wiederherstellung auf einem Staging-Server durchgeführt wird.

pg_dump -h localhost -U adminuser --data-only --column-inserts maindb > maindb.sql

Wie lösche ich zuerst alle Daten in der Staging-Server-Datenbank, bevor ich die Daten aus dem Produktionsspeicherauszug wiederherstelle?

Ich möchte nur alle Daten löschen, damit ich die Datenbank und all das Zeug nicht löschen und erstellen muss. Ich möchte nur Daten entfernen und neue Daten einfügen, das ist alles.

Ich habe aus mehreren Gründen nicht die Möglichkeit, die Datenbank zu löschen und zu erstellen. Ich muss alle Daten entfernen und nur einfügen. Was auch immer nötig ist, um herauszufinden, wie das geht, ich bin bereit, es zu versuchen, brauche aber natürlich zunächst Hilfe.

Ich muss diesen Prozess auch automatisieren. Automatisiert das "Speichern von Daten aus der Produktionsdatenbank", das "Löschen von Daten in der Staging-Datenbank" und das "Wiederherstellen von Daten in der Staging-Datenbank". Ich brauche nur Hilfe beim Teil "Löschen von Daten beim Staging von db".

Ich verwende PostgreSQL 9.5.2

Uberrebu
quelle

Antworten:

24

Sie müssen die Datenbank nicht löschen, es sollte ausreichen, alle Objekte in der Datenbank zu löschen. Dies kann mit erfolgen

drop owned by adminuser

Wenn Sie dann den SQL-Speicherauszug einschließlich der create tableAnweisungen erstellen (also ohne die --data-onlyOption), sollte alles in Ordnung sein.

Sie können dann auch das --column-insertsthen entfernen , wodurch der Import erheblich beschleunigt wird.


Wenn Sie jedoch alles löschen möchten, können Sie dies mit ein wenig dynamischem SQL tun:

do
$$
declare
  l_stmt text;
begin
  select 'truncate ' || string_agg(format('%I.%I', schemaname, tablename), ',')
    into l_stmt
  from pg_tables
  where schemaname in ('public');

  execute l_stmt;
end;
$$

Dadurch werden alle Tabellen im Schema publicmit einer einzigen Anweisung abgeschnitten, was auch dann funktioniert, wenn viele Fremdschlüsseleinschränkungen alle Tabellen verbinden. Wenn Ihre Tabellen auf mehrere Schemas verteilt sind, müssen Sie sie in der whereBedingung hinzufügen .

ein Pferd ohne Name
quelle
Ich verstehe ... macht es dasselbe wie @ypercube, wie oben erwähnt, um diesen Befehl zu verwenden TRUNCATE table1, table2, ... <list of all tables>;? machen beide dasselbe?
Uberrebu
1
@babababa: Ja, meine Antwort generiert diese Anweisung einfach und führt sie dynamisch aus, sodass Sie nicht alle Tabellennamen eingeben müssen. Wenn Sie eine neue Tabelle hinzufügen, wird diese automatisch eingeschlossen.
a_horse_with_no_name
schön, habe es gerade versucht und es funktioniert, @ypercube man funktioniert auch ... vielen Dank
uberrebu
6

pg_restore hat ein --clean-Flag (oder möglicherweise --create), das Daten automatisch löscht, bevor Operationen ausgeführt werden.

Die ausgezeichnete Dokumentation sollte Ihnen sehr helfen ...

Nur zur Klarstellung, falls es verwirrend ist:

Bereinigen Sie Datenbankobjekte , bevor Sie sie neu erstellen. (Wenn --if-exist nicht verwendet wird, kann dies zu harmlosen Fehlermeldungen führen, wenn keine Objekte in der Zieldatenbank vorhanden waren.)

Dadurch wird die eigentliche Datenbank nicht gelöscht. Nur die Tabellen / Ansichten / etc.

Wenn das Löschen und Neuerstellen der Tabellen aus irgendeinem Grund nicht akzeptabel ist, müssen Sie mehr Arbeit aufwenden, um manuell ein Skript zu erstellen, das einen data onlySpeicherauszug aus der Quelldatenbank, Problemen TRUNCATEoder DELETEin der Zieldatenbank erstellt Lädt den Datendump. Soweit mir bekannt ist, gibt es dafür keinen schnellen und raffinierten Weg.

Joishi Bodio
quelle
Wird das Flag --clean NUR Daten löschen und die Datenbank- und Tabellenstrukturen gleich, aber leer halten?
Uberrebu
Es wird eine Drop-Tabelle vor einer Create-Tabelle ausgegeben. Alle Tabellen, die in der Dump-Datei vorhanden sind. Ich würde hoffen, dass die Dump-Datei die Informationen enthält, um die Tabelle genau so neu zu erstellen, wie sie zuvor existiert hat (einschließlich FKeys usw.). Aber es hängt wirklich davon ab, wie Sie die Dump-Datei erstellt haben. Da Sie jedoch immer wieder von "Staging" sprechen, scheint es, als ob Sie wirklich nach einer Möglichkeit suchen, Staging-Tabellen in einem Data Warehouse mit Daten aus einer Produktionsdatenbank zu füllen. Wenn das Ihr Ziel ist, ist eine Dump-Datei wahrscheinlich der falsche Ansatz.
Joishi Bodio
Das ist nicht das, was ich tun möchte, ich möchte nur Daten löschen. Die Datenbank- und Tabellenstruktur bleibt gleich und unberührt. Meine Frage ist ziemlich klar, was ich tun möchte, selbst aus dem Titel
uberrebu
Dann wird Ihre Lösung leider viel schwieriger.
Joishi Bodio
3
SELECT 'TRUNCATE ' || input_table_name || ' CASCADE;' AS truncate_query FROM(SELECT table_schema || '.' || table_name AS input_table_name FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog', 'information_schema') AND table_schema NOT LIKE 'pg_toast%') AS information;  

Die obige Abfrage generiert abgeschnittene Abfragen für alle Tabellen in der Datenbank.

Thirumal
quelle
0

Hinweis: In meiner Antwort geht es darum, die Tabellen und andere Datenbankobjekte wirklich zu löschen. Zum Löschen aller Daten in den Tabellen, dh zum Abschneiden aller Tabellen , hat Endre Both einen Monat später eine ähnlich gut ausgeführte Anweisung (direkte Ausführung) bereitgestellt.

Für die Fälle , in denen Sie nicht können DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;oder etwas, hier ist ein Stand-alone - SQL - Skript schrieb ich, die transaktionssicher ist (dh Sie es zwischen setzen können BEGIN;und entweder ROLLBACK;nur testen Sie es aus oder COMMIT;tatsächlich zu tun , die Tat) und Bereinigt "alle" Datenbankobjekte ... nun, alle, die in der Datenbank verwendet werden, die unsere Anwendung verwendet, oder ich könnte vernünftigerweise hinzufügen, nämlich:

  • Trigger auf Tabellen
  • Einschränkungen für Tabellen (FK, PK, CHECK, UNIQUE)
  • indicēs
  • VIEWs (normal oder materialisiert)
  • Tabellen
  • Sequenzen
  • Routinen (Aggregatfunktionen, Funktionen, Prozeduren)
  • Alle nicht standardmäßigen (dh nicht publicoder DB-internen) Schemata, die „wir“ besitzen: Das Skript ist nützlich, wenn es als „kein Datenbank-Superuser“ ausgeführt wird. Ein Superuser kann alle Schemata löschen (die wirklich wichtigen sind jedoch immer noch ausdrücklich ausgeschlossen).
  • Erweiterungen (vom Benutzer beigesteuert, aber ich lasse sie normalerweise absichtlich in)

Nicht fallen gelassen werden (einige absichtlich; einige nur, weil ich kein Beispiel in unserer DB hatte):

  • das publicSchema (z. B. für von Erweiterungen bereitgestellte Inhalte)
  • Kollatierungen und andere Gebietsschemata
  • Ereignisauslöser
  • Textsuche,… (siehe hier für andere Sachen, die ich vielleicht verpasst habe)
  • Rollen oder andere Sicherheitseinstellungen
  • zusammengesetzte Typen
  • Toasttische
  • FDW und fremde Tabellen

Dies ist sehr nützlich für Fälle, in denen der Dump, den Sie wiederherstellen möchten, eine andere Datenbankschemaversion aufweist (z. B. mit Debian dbconfig-common, Flyway oder Liquibase / DB-Manul) als die Datenbank, in der Sie ihn wiederherstellen möchten.

Ich habe auch eine Version, die "alles außer zwei Tabellen und was dazu gehört" löscht (eine Sequenz, manuell getestet, sorry, ich weiß, langweilig), falls jemand interessiert ist; Der Unterschied ist klein. Kontaktieren Sie mich oder überprüfen Sie dieses Repo bei Interesse.

SQL

-- Copyright © 2019, 2020
--      mirabilos <[email protected]>
--
-- Provided that these terms and disclaimer and all copyright notices
-- are retained or reproduced in an accompanying document, permission
-- is granted to deal in this work without restriction, including un‐
-- limited rights to use, publicly perform, distribute, sell, modify,
-- merge, give away, or sublicence.
--
-- This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
-- the utmost extent permitted by applicable law, neither express nor
-- implied; without malicious intent or gross negligence. In no event
-- may a licensor, author or contributor be held liable for indirect,
-- direct, other damage, loss, or other issues arising in any way out
-- of dealing in the work, even if advised of the possibility of such
-- damage or existence of a defect, except proven that it results out
-- of said person’s immediate fault when using the work as intended.
-- -
-- Drop everything from the PostgreSQL database.

DO $$
DECLARE
        q TEXT;
        r RECORD;
BEGIN
        -- triggers
        FOR r IN (SELECT pns.nspname, pc.relname, pt.tgname
                FROM pg_catalog.pg_trigger pt, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pt.tgrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pt.tgisinternal=false
            ) LOOP
                EXECUTE format('DROP TRIGGER %I ON %I.%I;',
                    r.tgname, r.nspname, r.relname);
        END LOOP;
        -- constraints #1: foreign key
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype='f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- constraints #2: the rest
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype<>'f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- indicēs
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='i'
            ) LOOP
                EXECUTE format('DROP INDEX %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- normal and materialised views
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind IN ('v', 'm')
            ) LOOP
                EXECUTE format('DROP VIEW %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- tables
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='r'
            ) LOOP
                EXECUTE format('DROP TABLE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- sequences
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='S'
            ) LOOP
                EXECUTE format('DROP SEQUENCE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- extensions (only if necessary; keep them normally)
        FOR r IN (SELECT pns.nspname, pe.extname
                FROM pg_catalog.pg_extension pe, pg_catalog.pg_namespace pns
                WHERE pns.oid=pe.extnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
            ) LOOP
                EXECUTE format('DROP EXTENSION %I;', r.extname);
        END LOOP;
        -- aggregate functions first (because they depend on other functions)
        FOR r IN (SELECT pns.nspname, pp.proname, pp.oid
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns, pg_catalog.pg_aggregate pagg
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pagg.aggfnoid=pp.oid
            ) LOOP
                EXECUTE format('DROP AGGREGATE %I.%I(%s);',
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- routines (functions, aggregate functions, procedures, window functions)
        IF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='prokind' -- PostgreSQL 11+
            ) THEN
                q := 'CASE pp.prokind
                        WHEN ''p'' THEN ''PROCEDURE''
                        WHEN ''a'' THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSIF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='proisagg' -- PostgreSQL ≤10
            ) THEN
                q := 'CASE pp.proisagg
                        WHEN true THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSE
                q := '''FUNCTION''';
        END IF;
        FOR r IN EXECUTE 'SELECT pns.nspname, pp.proname, pp.oid, ' || q || ' AS pt
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN (''information_schema'', ''pg_catalog'', ''pg_toast'')
            ' LOOP
                EXECUTE format('DROP %s %I.%I(%s);', r.pt,
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- nōn-default schemata we own; assume to be run by a not-superuser
        FOR r IN (SELECT pns.nspname
                FROM pg_catalog.pg_namespace pns, pg_catalog.pg_roles pr
                WHERE pr.oid=pns.nspowner
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'public')
                    AND pr.rolname=current_user
            ) LOOP
                EXECUTE format('DROP SCHEMA %I;', r.nspname);
        END LOOP;
        -- voilà
        RAISE NOTICE 'Database cleared!';
END; $$;

Getestet, mit Ausnahme späterer Ergänzungen ( extensionsbeigesteuert von Clément Prévost ), auf PostgreSQL 9.6 ( jessie-backports). Die Aggregatentfernung wurde unter 9.6 und 12.2 getestet, die Verfahrensentfernung ebenfalls unter 12.2. Bugfixes und weitere Verbesserungen sind willkommen!

Mirabilos
quelle
Perfekt, hier ist mein Code für die Erweiterungen, er muss vor Funktionen / Prozeduren stehen: - Erweiterungen FOR r IN (SELECT pns.nspname, pe.extname FROM pg_extension pe, pg_namespace pns WHERE pns.oid = pe.extnamespace AND pns .nspname NOT IN ('Informationsschema', 'pg_catalog', 'pg_toast')) LOOP EXECUTE-Format ('DROP EXTENSION% I;', r.extname); END LOOP;
Clément Prévost
@ ClémentPrévost danke, ich habe Ihren Kommentar in den Code eingefügt (hoffe, ich habe es richtig gemacht, Kommentare fehlen formatiert, bitte überprüfen). Normalerweise lasse ich Erweiterungen absichtlich nicht gelöscht (mein Anwendungsfall ist das Wiederherstellen von Sicherungen mit unterschiedlichen Schemaversionen, und normalerweise ist genau eine Erweiterung, PL / pgSQL, geladen). Es könnte jedoch für einige nützlich sein, also danke!
Mirabilos
Perfekt, danke :)
Clément Prévost