web-dev-qa-db-fra.com

Supprimer toutes les données de la base de données Postgres

J'ai créé un nouveau db db à partir d'un serveur de production avec le --data-only et --column-inserts flags, donc je n'ai qu'un tas d'instructions d'insertion pour insérer des données lors d'une restauration sur un serveur intermédiaire.

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

Comment puis-je supprimer toutes les données de la base de données du serveur de transfert avant de restaurer les données à partir du vidage de production?

Je veux supprimer toutes les données uniquement afin de ne pas avoir à supprimer et à créer la base de données et tout ça. Je veux juste supprimer des données et insérer de nouvelles données, c'est tout.

Je n'ai pas la possibilité de supprimer et de créer la base de données pour plusieurs raisons. Je vais devoir supprimer toutes les données et les insérer uniquement, donc tout ce qu'il faut pour trouver comment faire, je suis prêt à y aller, mais j'ai évidemment besoin d'aide pour commencer.

J'ai également besoin d'automatiser ce processus. Automatisera le "vidage des données de la base de données de production", puis "la suppression des données sur la base de données intermédiaire", puis "la restauration des données vers la base de données intermédiaire". J'ai juste besoin d'aide sur la partie "suppression de données sur la base de données intermédiaire".

Je cours sur PostgreSQL 9.5.2

14
uberrebu

Vous n'avez pas besoin de supprimer la base de données, il devrait suffire de supprimer tous les objets dans la base de données. Cela peut être fait en utilisant

drop owned by adminuser

Si vous créez ensuite le vidage SQL comprenant le create table instructions (donc sans les --data-only option) tout devrait bien se passer.

Vous pouvez également supprimer le --column-inserts alors, ce qui accélérera l'importation lot.


Cependant, si vous voulez tout supprimer, vous pouvez le faire avec un peu de SQL dynamique:

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

Cela tronquera toutes les tables du schéma public avec une seule instruction qui fonctionnera également même s'il existe de nombreuses contraintes de clé étrangère reliant toutes les tables. Si vos tables sont réparties sur plusieurs schémas, vous devez les ajouter dans la condition where.

24

pg_restore a un indicateur --clean (ou éventuellement --create) qui supprimera automatiquement les données avant d'exécuter les opérations.

L'excellente documentation devrait vous aider grandement ...

Juste pour clarifier, au cas où cela prêterait à confusion:

Nettoyez (supprimez) les objets de la base de données avant de les recréer. (À moins que --si existe existe, cela peut générer des messages d'erreur inoffensifs, si aucun objet n'était présent dans la base de données de destination.)

Cela ne supprimera pas la base de données réelle .. uniquement les tables/vues/etc.

Si, pour une raison quelconque, la suppression et la recréation des tables ne sont pas acceptables, vous devrez mettre plus de travail pour créer manuellement un script qui crée un data only vidage à partir de la base de données source, émet TRUNCATE ou DELETE dans la base de données cible, puis charge le vidage des données. Pour autant que je sache, il n'y a pas de façon rapide/simple de le faire.

6
Joishi Bodio
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;  

La requête ci-dessus générera des requêtes tronquées pour toutes les tables de la base de données.

3
Thirumal

Remarque: ma réponse consiste à supprimer réellement les tables et autres objets de base de données; pour suppression de toutes les données in les tables, c'est-à-dire la troncature de toutes les tables , Endre Both a fourni une déclaration également bien exécutée (exécution directe) un mois plus tard.

Pour les cas où vous ne pouvez pas simplement DROP SCHEMA public CASCADE;, DROP OWNED BY current_user; ou quelque chose comme ça, voici un script SQL autonome que j'ai écrit, qui est sûr pour les transactions (c'est-à-dire que vous pouvez le mettre entre BEGIN; et soit ROLLBACK; pour le tester ou COMMIT; pour faire l'acte) et nettoie "tous" les objets de la base de données… enfin, tous ceux utilisés dans la base de données que notre application utilise ou je pourrais raisonnablement ajouter, ce qui est:

  • déclencheurs sur les tables
  • contraintes sur les tables (FK, PK, CHECK, UNIQUE)
  • indicēs
  • VIEWs (normal ou matérialisé)
  • les tables
  • séquences
  • routines (fonctions agrégées, fonctions, procédures)
  • tous les schémas nōn-default (c'est-à-dire pas public ou DB-internal) "que nous" possédons: le script est utile lorsqu'il est exécuté comme "pas un superutilisateur de base de données"; un superutilisateur peut supprimer tous les schémas (les plus importants sont toujours explicitement exclus, cependant)
  • extensions (fournies par l'utilisateur, mais je les laisse normalement délibérément)

Non abandonnés sont (certains délibérés; certains uniquement parce que je n'avais aucun exemple dans notre DB):

  • le schéma public (par exemple pour les éléments fournis par l'extension)
  • des classements et d'autres trucs locaux
  • déclencheurs d'événements
  • recherche de texte,… (voir ici pour d'autres choses que j'aurais pu manquer)
  • rôles ou autres paramètres de sécurité
  • types composites
  • tables de toast
  • FDW et tables étrangères

C'est vraiment utile dans les cas où le vidage que vous souhaitez restaurer est d'une version de schéma de base de données différente (par exemple avec Debian dbconfig-common, Flyway ou Liquibase/DB-Manul) que la base de données dans laquelle vous souhaitez le restaurer.

J'ai également une version qui supprime "tout sauf deux tables et ce qui leur appartient" (une séquence, testée manuellement, désolé, je sais, ennuyeuse) au cas où quelqu'un serait intéressé; le diff est petit. Contactez-moi ou vérifiez ce dépôt si vous êtes intéressé.

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

Testé, sauf ajouts ultérieurs (extensions apporté par Clément Prévost ), sur PostgreSQL 9.6 (jessie-backports). Suppression d'agrégats testée sur 9.6 et 12.2, retrait de procédure également testé sur 12.2. Les corrections de bugs et autres améliorations sont les bienvenues!

0
mirabilos