Abschneiden aller Tabellen in einer Postgres-Datenbank

155

Ich muss vor einer Neuerstellung regelmäßig alle Daten aus meiner PostgreSQL-Datenbank löschen. Wie würde ich das direkt in SQL machen?

Im Moment habe ich es geschafft, eine SQL-Anweisung zu erstellen, die alle Befehle zurückgibt, die ich zum Ausführen benötige:

SELECT 'TRUNCATE TABLE ' ||  tablename || ';' FROM pg_tables WHERE tableowner='MYUSER';

Aber ich sehe keine Möglichkeit, sie programmgesteuert auszuführen, sobald ich sie habe.

Sig
quelle

Antworten:

226

FrustratedWithFormsDesigner ist korrekt, PL / pgSQL kann dies tun. Hier ist das Skript:

CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$
DECLARE
    statements CURSOR FOR
        SELECT tablename FROM pg_tables
        WHERE tableowner = username AND schemaname = 'public';
BEGIN
    FOR stmt IN statements LOOP
        EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' CASCADE;';
    END LOOP;
END;
$$ LANGUAGE plpgsql;

Dadurch wird eine gespeicherte Funktion erstellt (Sie müssen dies nur einmal tun), die Sie anschließend wie folgt verwenden können:

SELECT truncate_tables('MYUSER');
Henning
quelle
1
Musste ein wenig rejig, aber danach funktionierte es wie ein Zauber! Ich habe noch nie zuvor plpgsql verwendet, daher hätte ich ewig gebraucht. Vielen Dank! Für alle, die es brauchen, habe ich den Code, den ich letztendlich verwendet habe, am Ende dieses Beitrags hinzugefügt.
Sig
Entschuldigung, ich habe wahrscheinlich in Oracle PL / SQL gedacht :( Ich habe den Syntaxfehler in meinem obigen Code behoben.
Henning
1
Sie können die SELECT-Anweisung auch direkt in die FOR-Schleife verschieben. DECLARE r RECORD;dann für Schleife: FOR r IN SELECT tablename FROM pg_tables LOOP
Michael Buen
6
Ich würde CASCADE zu TRUNCATE TABLE hinzufügen
Bogdan Gusiev
3
OH MEIN GOTT!! Ich habe gerade alle meine Tabellen im "öffentlichen" Schema abgeschnitten. Bitte fügen Sie einen weiteren Parameter von "Schema" hinzu, damit die Funktion Tabellen nur für das bereitgestellte Schema abschneidet!
Roneo
95

Explizite Cursor werden in plpgsql selten benötigt. Verwenden Sie den einfacheren und schnelleren impliziten Cursor einer FORSchleife:

Hinweis: Da Tabellennamen pro Datenbank nicht eindeutig sind, müssen Sie Tabellennamen schemaqualifizieren, um sicherzugehen. Außerdem beschränke ich die Funktion auf das Standardschema 'public'. Passen Sie sich Ihren Anforderungen an, schließen Sie jedoch die Systemschemata pg_*und aus information_schema.

Seien Sie sehr vorsichtig mit diesen Funktionen. Sie zerstören Ihre Datenbank. Ich habe eine Kindersicherung hinzugefügt. Kommentieren Sie die RAISE NOTICELinie und kommentieren Sie, EXECUTEum die Bombe zu zünden ...

CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
  RETURNS void AS
$func$
DECLARE
   _tbl text;
   _sch text;
BEGIN
   FOR _sch, _tbl IN 
      SELECT schemaname, tablename
      FROM   pg_tables
      WHERE  tableowner = _username
      AND    schemaname = 'public'
   LOOP
      RAISE NOTICE '%',
      -- EXECUTE  -- dangerous, test before you execute!
         format('TRUNCATE TABLE %I.%I CASCADE', _sch, _tbl);
   END LOOP;
END
$func$ LANGUAGE plpgsql;

format()erfordert Postgres 9.1 oder höher. In älteren Versionen verketten Sie die Abfragezeichenfolge wie folgt:

'TRUNCATE TABLE ' || quote_ident(_sch) || '.' || quote_ident(_tbl)  || ' CASCADE';

Einzelbefehl, keine Schleife

Da wir TRUNCATEmehrere Tabellen gleichzeitig erstellen können, benötigen wir überhaupt keinen Cursor oder keine Schleife:

Aggregieren Sie alle Tabellennamen und führen Sie eine einzelne Anweisung aus. Einfacher, schneller:

CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
  RETURNS void AS
$func$
BEGIN
   RAISE NOTICE '%', 
   -- EXECUTE  -- dangerous, test before you execute!
  (SELECT 'TRUNCATE TABLE '
       || string_agg(format('%I.%I', schemaname, tablename), ', ')
       || ' CASCADE'
   FROM   pg_tables
   WHERE  tableowner = _username
   AND    schemaname = 'public'
   );
END
$func$ LANGUAGE plpgsql;

Anruf:

SELECT truncate_tables('postgres');

Verfeinerte Abfrage

Sie brauchen nicht einmal eine Funktion. In Postgres 9.0+ können Sie dynamische Befehle in einer DOAnweisung ausführen . Und in Postgres 9.5+ kann die Syntax noch einfacher sein:

DO
$func$
BEGIN
   RAISE NOTICE '%', 
   -- EXECUTE
   (SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
    FROM   pg_class
    WHERE  relkind = 'r'  -- only tables
    AND    relnamespace = 'public'::regnamespace
   );
END
$func$;

Über den Unterschied zwischen pg_class, pg_tablesund information_schema.tables:

Über regclassund zitierte Tabellennamen:

Für den wiederholten Gebrauch

Erstellen Sie eine "Vorlagendatenbank" (nennen wir es my_template) mit Ihrer Vanillestruktur und allen leeren Tabellen. Dann gehen Sie durch einen DROP/CREATE DATABASE Zyklus:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

Das geht extrem schnell , da Postgres die gesamte Struktur auf Dateiebene kopiert. Keine Parallelitätsprobleme oder anderer Overhead, der Sie verlangsamt.

Wenn gleichzeitige Verbindungen Sie davon abhalten, die Datenbank zu löschen, beachten Sie Folgendes:

Erwin Brandstetter
quelle
1
Es ist erwähnenswert, dass diese letzte Funktion ALLE Datenbanken gelöscht hat. Nicht nur der aktuell verbundene ... ja ... nenn mich naiive, aber das war in diesem Beitrag wirklich nicht klar.
Amalgovinus
@Amalgovinus: Welche letzte Funktion? Keine der Funktionen in meiner Antwort berührt etwas außerhalb der aktuellen Datenbank (außer DROP DATABASE mydbnatürlich). Verwechseln Sie Schemata vielleicht mit Datenbanken?
Erwin Brandstetter
3
@Amalgovinus: Nein, das ist unmöglich. Der DOBefehl wird (wie jede andere SQL-Anweisung) ausschließlich in der aktuellen Datenbank ausgeführt . Postgres hat keine Möglichkeit, in derselben Transaktion auf andere Datenbanken zuzugreifen. Sie müssten dazu dblink oder FDW verwenden. Es wirkt sich jedoch auf alle Schemas in der aktuellen Datenbank aus - es sei denn, Sie fügen hinzu WHERE t.schemaname = 'public', um den Effekt in diesem speziellen Fall auf ein bestimmtes Schema zu beschränken.
Erwin Brandstetter
1
Wirklich schön über diese Vorlagen zu wissen. Dies kann mir auch in automatisierten Testszenarien nützlich sein, in denen möglicherweise ein Zurücksetzen / Vorbereiten der Datenbank erforderlich ist.
Hbobenicio
3
Vielen Dank für die gute Antwort. Ich verwende "Einzelbefehl, keine Schleife", die den Befehl TRUNCATE zurückgibt. Wie soll ich vorgehen, um ihn auszuführen?
Mahyar
40

Wenn ich dies tun muss, erstelle ich einfach ein Schema-SQL der aktuellen Datenbank, lösche und erstelle die Datenbank und lade dann die Datenbank mit dem Schema-SQL.

Nachfolgend sind die Schritte aufgeführt:

1) Erstellen Sie einen Schema-Dump der Datenbank ( --schema-only)

pg_dump mydb -s > schema.sql

2) Datenbank löschen

drop database mydb;

3) Datenbank erstellen

create database mydb;

4) Schema importieren

psql mydb < schema.sql

Sandip Ransing
quelle
9

In diesem Fall ist es wahrscheinlich besser, nur eine leere Datenbank zu haben, die Sie als Vorlage verwenden. Wenn Sie eine Aktualisierung benötigen, löschen Sie die vorhandene Datenbank und erstellen Sie eine neue aus der Vorlage.

Scott Bailey
quelle
3

Sie können dies auch mit bash tun:

#!/bin/bash
PGPASSWORD='' psql -h 127.0.0.1 -Upostgres sng --tuples-only --command "SELECT 'TRUNCATE TABLE ' || schemaname || '.' ||  tablename || ';' FROM pg_tables WHERE schemaname in ('cms_test', 'ids_test', 'logs_test', 'sps_test');" | 
tr "\\n" " " | 
xargs -I{} psql -h 127.0.0.1 -Upostgres sng --command "{}"

Sie müssen Schemanamen, Kennwörter und Benutzernamen an Ihre Schemas anpassen.

simao
quelle
3

Reinigungsversion AUTO_INCREMENT:

CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$
DECLARE
    statements CURSOR FOR
        SELECT tablename FROM pg_tables
        WHERE tableowner = username AND schemaname = 'public';
BEGIN
    FOR stmt IN statements LOOP
        EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' CASCADE;';

        IF EXISTS (
            SELECT column_name 
            FROM information_schema.columns 
            WHERE table_name=quote_ident(stmt.tablename) and column_name='id'
        ) THEN
           EXECUTE 'ALTER SEQUENCE ' || quote_ident(stmt.tablename) || '_id_seq RESTART WITH 1';
        END IF;

    END LOOP;
END;
$$ LANGUAGE plpgsql;
RomanGorbatko
quelle
3

Jungs, der bessere und sauberere Weg ist:

1) Erstellen Sie einen Schema-Dump der Datenbank (nur --schema-only) pg_dump mydb -s> schema.sql

2) Datenbank löschen Datenbank löschen mydb;

3) Datenbank erstellen Datenbank erstellen mydb;

4) Importieren Sie Schema psql mydb <schema.sql

Es funktioniert für mich!

Einen schönen Tag noch. Hiram Walker

Hiram Walker
quelle
2

Wenn Sie psql verwenden können, können Sie den \gexecMeta-Befehl verwenden, um die Abfrageausgabe auszuführen.

SELECT
    format('TRUNCATE TABLE %I.%I', ns.nspname, c.relname)
  FROM pg_namespace ns 
  JOIN pg_class c ON ns.oid = c.relnamespace
  JOIN pg_roles r ON r.oid = c.relowner
  WHERE
    ns.nspname = 'table schema' AND                               -- add table schema criteria 
    r.rolname = 'table owner' AND                                 -- add table owner criteria
    ns.nspname NOT IN ('pg_catalog', 'information_schema') AND    -- exclude system schemas
    c.relkind = 'r' AND                                           -- tables only
    has_table_privilege(c.oid, 'TRUNCATE')                        -- check current user has truncate privilege
  \gexec 

Beachten Sie, dass dies \gexecin Version 9.6 eingeführt wird

Sahap Asci
quelle
1

Um die Daten zu entfernen und die Tabellenstrukturen in pgAdmin beizubehalten, haben Sie folgende Möglichkeiten :

  • Klicken Sie mit der rechten Maustaste auf Datenbank -> Sicherung, wählen Sie "Nur Schema".
  • Löschen Sie die Datenbank
  • Erstellen Sie eine neue Datenbank und benennen Sie sie wie die vorherige
  • Klicken Sie mit der rechten Maustaste auf die neue Datenbank -> Wiederherstellen -> wählen Sie die Sicherung aus und wählen Sie "Nur Schema".
mYnDstrEAm
quelle