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


14

J'ai créé un nouveau vidage de base de données à partir d'un serveur de production avec les indicateurs --data-onlyet --column-inserts, 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 pour 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

Réponses:


24

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

drop owned by adminuser

Si vous créez ensuite le vidage SQL, y compris les create tableinstructions (donc sans l' --data-onlyoption), tout devrait bien se passer .

Vous pouvez également supprimer le --column-insertsthen, ce qui rendra l'importation beaucoup plus rapide.


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 publicavec 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 wherecondition.


je vois ... cela fait-il la même chose que @ypercube mentionné ci-dessus pour utiliser cette commande TRUNCATE table1, table2, ... <list of all tables>;? font-ils tous les deux la même chose?
uberrebu

1
@babababa: oui, ma réponse génère et exécute simplement cette instruction de manière dynamique, vous n'avez donc pas à taper tous les noms de table et si vous ajoutez une nouvelle table, elle sera automatiquement incluse.
a_horse_with_no_name

nice vient de l'essayer et cela fonctionne, @ypercube on fonctionne aussi ... merci beaucoup
uberrebu

6

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 base de données avant de les recréer. (À moins que --si 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 onlyvidage à partir de la base de données source, des problèmes TRUNCATEou DELETEde la base de données cible, puis charge le vidage de données. Pour autant que je sache, il n'y a pas de façon rapide / simple de le faire.


cet indicateur --clean supprimera-t-il UNIQUEMENT les données et gardera les structures de base de données et de tables identiques mais vides?
uberrebu

Il émettra une table de dépôt avant une table de création. Toutes les tables qui existent dans le fichier de vidage. J'espère que le fichier de vidage contient les informations pour recréer la table exactement comme elle existait auparavant (y compris les FKeys, etc.). Mais cela dépend vraiment de la façon dont vous avez créé le fichier de vidage. Cependant, étant donné que vous continuez à mentionner le "transfert", il semble que ce que vous cherchez vraiment soit un moyen de remplir les tables de transfert dans un entrepôt de données avec des données provenant d'une base de données de production. Si tel est votre objectif, un fichier de vidage est probablement la mauvaise approche.
Joishi Bodio

ce n'est pas ce que je cherche à faire, je veux juste supprimer les données ... la structure des bases de données et des tables restera la même et intacte ... ma question est assez claire sur ce que je veux faire, même d'après le titre
uberrebu

Ensuite, désolé de le dire, votre solution va être beaucoup plus difficile.
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;  

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


0

Remarque: ma réponse consiste à supprimer réellement les tables et autres objets de base de données; pour la suppression de toutes les données dans les tableaux, à savoir tronquer toutes les tables , Endre deux a fourni une déclaration (exécution directe) Similairement bien exécuté 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 le ROLLBACK;tester simplement, soit COMMIT;faire l'acte) et nettoie «tous» les objets de la base de données… eh bien, tous ceux utilisés dans la base de données que notre application utilise ou je pourrais raisonnablement ajouter, à savoir:

  • déclencheurs sur les tables
  • les 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 par défaut (c'est-à-dire non publicou internes à la base de données) 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 cependant explicitement exclus)
  • 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 publicschéma (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

Ceci 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 consultez ce repo si vous êtes intéressé.

SQL

-- Copyright © 2019, 2020
--      mirabilos <t.glaser@tarent.de>
--
-- 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 ( extensionscontribution de 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!


Parfait, voici mon code pour les extensions, il doit être placé avant les fonctions / procédures: - extensions 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 ('information_schema', 'pg_catalog', 'pg_toast')) LOOP EXECUTE format ('DROP EXTENSION% I;', r.extname); END LOOP;
Clément Prévost

@ Merci ClémentPrévost, j'ai fusionné votre commentaire dans le code (j'espère l'avoir bien fait, les commentaires manquent de mise en forme, merci de revoir). Je laisse normalement les extensions non supprimées délibérément (mon cas d'utilisation est la restauration à partir de sauvegardes avec différentes versions de schéma, et j'ai normalement exactement une extension, PL / pgSQL, chargée). Cela pourrait être utile à certains, cependant, merci!
mirabilos

Parfait, merci :)
Clément Prévost
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.