Troncare tutte le tabelle in un database Postgres


155

Ho regolarmente bisogno di eliminare tutti i dati dal mio database PostgreSQL prima di una ricostruzione. Come lo farei direttamente in SQL?

Al momento sono riuscito a trovare un'istruzione SQL che restituisce tutti i comandi che devo eseguire:

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

Ma non riesco a vedere un modo per eseguirli a livello di programmazione una volta che li ho.

Risposte:


226

FrustratedWithFormsDesigner è corretto, PL / pgSQL può farlo. Ecco la sceneggiatura:

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;

Questo crea una funzione memorizzata (devi farlo solo una volta) che puoi successivamente usare in questo modo:

SELECT truncate_tables('MYUSER');

1
Ho dovuto gioire un po 'ma dopo ha funzionato come un incantesimo! Non ho mai usato plpgsql prima quindi questo mi avrebbe impiegato anni. Grazie! Per chiunque ne abbia bisogno ho aggiunto il codice che ho finito per usare in fondo a questo post.
Sig

Scusa, probabilmente stavo pensando in Oracle PL / SQL :( ho corretto l'errore di sintassi nel mio codice sopra.
Henning

1
puoi anche spostare l'istruzione SELECT direttamente nel ciclo FOR. DECLARE r RECORD;poi per loop: FOR r IN SELECT tablename FROM pg_tables LOOP
Michael Buen,

6
Aggiungerei CASCADE alla TABELLA TRUNCATE
Bogdan Gusiev

3
OH MIO DIO!! Ho appena troncato tutte le mie tabelle nello schema "pubblico" .... per favore aggiungi un altro parametro di "schema" in modo che la funzione tronchi le tabelle solo sullo schema che viene fornito!
roneo,

95

I cursori espliciti sono raramente necessari in plpgsql. Usa il cursore implicito più semplice e veloce di un FORloop:

Nota: poiché i nomi delle tabelle non sono univoci per database, per essere sicuri, è necessario qualificare lo schema per i nomi delle tabelle. Inoltre, limito la funzione allo schema predefinito "pubblico". Adattarsi alle proprie esigenze, ma assicurarsi di escludere gli schemi di sistema pg_*e information_schema.

Stai molto attento con queste funzioni. Nuotano il tuo database. Ho aggiunto un dispositivo di sicurezza per bambini. Commenta la RAISE NOTICElinea e il commento EXECUTEper adescare la bomba ...

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()richiede Postgres 9.1 o successivo. Nelle versioni precedenti concatenare la stringa di query in questo modo:

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

Singolo comando, nessun loop

Dal momento che possiamo TRUNCATEpiù tabelle contemporaneamente non abbiamo bisogno di alcun cursore o ciclo:

Aggrega tutti i nomi di tabella ed esegue una singola istruzione. Più semplice, più veloce:

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;

Chiamata:

SELECT truncate_tables('postgres');

Query raffinata

Non hai nemmeno bisogno di una funzione. In Postgres 9.0+ puoi eseguire comandi dinamici in DOun'istruzione. E in Postgres 9.5+ la sintassi può essere ancora più semplice:

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$;

Circa la differenza tra pg_class, pg_tablese information_schema.tables:

Informazioni sui regclassnomi di tabella citati:

Per uso ripetuto

Crea un database "modello" (chiamiamolo my_template) con la tua struttura vanilla e tutte le tabelle vuote. Quindi passare attraverso un DROP/CREATE DATABASE ciclo:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

Questo è estremamente veloce , perché Postgres copia l'intera struttura a livello di file. Nessun problema di concorrenza o altri costi generali che ti rallentano.

Se connessioni simultanee ti impediscono di eliminare il DB, considera:


1
Vale la pena notare che quest'ultima funzione ha cancellato TUTTI i database. Non solo quello attualmente connesso .... sì ... chiamami naiive, ma questo non era davvero chiaro da questo post.
Amalgovinus,

@Amalgovinus: quale ultima funzione? Nessuna delle funzioni nella mia risposta tocca qualcosa al di fuori del database corrente (tranne DROP DATABASE mydbovviamente). Stai confondendo gli schemi con i database, forse?
Erwin Brandstetter,

3
@Amalgovinus: No, è impossibile. Il DOcomando (come qualsiasi altra istruzione SQL) viene eseguito esclusivamente nel database corrente . Postgres non ha modo di accedere ad altri database nella stessa transazione. Dovresti usare dblink o FDW per farlo. Ma fa effetto su tutti gli schemi nel database corrente - se non si aggiunge WHERE t.schemaname = 'public'per limitare l'effetto di un particolare schema in questo caso particolare.
Erwin Brandstetter,

1
Davvero bello sapere di quei modelli. Questo può essere utile anche in scenari di test automatizzati, in cui potrebbe essere necessario un ripristino / preparazione del database.
hbobenicio,

3
Grazie per un'ottima risposta, sto usando "comando singolo, nessun ciclo" che restituisce il comando TRUNCATE, come devo fare per eseguirlo?
Mahyar,

40

Se devo farlo, creerò semplicemente uno schema sql del db corrente, quindi rilasciarò e creerò db, quindi caricherò db con schema sql.

Di seguito sono riportati i passaggi coinvolti:

1) Crea schema dump del database ( --schema-only)

pg_dump mydb -s > schema.sql

2) Rilascia database

drop database mydb;

3) Crea database

create database mydb;

4) Schema di importazione

psql mydb < schema.sql


9

In questo caso, probabilmente sarebbe meglio avere solo un database vuoto che si utilizza come modello e quando è necessario aggiornare, eliminare il database esistente e crearne uno nuovo dal modello.



3

Puoi farlo anche con bash:

#!/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 "{}"

Dovrai adattare nomi, password e nomi utente dello schema in modo che corrispondano ai tuoi schemi.


3

AUTO_INCREMENTVersione di pulizia :

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;

3

Ragazzi, il modo migliore e pulito è:

1) Crea schema dump del database (--schema-only) pg_dump mydb -s> schema.sql

2) Drop database drop database mydb;

3) Crea database crea database mydb;

4) Importa schema psql mydb <schema.sql

Per me funziona!

Buona giornata. Hiram Walker


2

Se puoi usare psql è possibile utilizzare il \gexecmeta comando per eseguire l'output della query;

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 

Si noti che \gexecè stato introdotto nella versione 9.6


1

Per rimuovere i dati e preservare le strutture delle tabelle in pgAdmin puoi fare:

  • Database tasto destro del mouse -> backup, selezionare "Solo schema"
  • Rilascia il database
  • Crea un nuovo database e chiamalo come il primo
  • Fai clic con il pulsante destro del mouse sul nuovo database -> ripristina -> seleziona il backup, seleziona "Solo schema"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.