Eliminar todos los datos en la base de datos Postgres

14

He creado un nuevo volcado de base de datos desde un servidor de producción con los indicadores --data-onlyy --column-inserts, por lo que solo tengo un montón de instrucciones de inserción para insertar datos cuando realizo una restauración en un servidor intermedio.

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

¿Cómo elimino primero todos los datos en la base de datos del servidor de ensayo, antes de restaurar los datos del volcado de producción?

Solo quiero eliminar todos los datos para no tener que soltar y crear la base de datos y todo eso. Solo quiero eliminar datos e insertar nuevos datos, eso es todo.

No tengo la opción de soltar y crear la base de datos por varias razones. Tendré que eliminar todos los datos y solo insertarlos, así que sea lo que sea necesario para encontrar la forma de hacerlo, estoy dispuesto a hacerlo, pero obviamente necesito ayuda para comenzar.

También necesito automatizar este proceso. Automatizará "volcar datos de producción de base de datos", luego "eliminar datos en la preparación de base de datos" y luego "restaurar los datos en la preparación de base de datos". Solo necesito ayuda en la parte "eliminar datos en la preparación de db".

Estoy corriendo en PostgreSQL 9.5.2

uberrebu
fuente

Respuestas:

24

No tiene que soltar la base de datos, debería ser suficiente para soltar todos los objetos en la base de datos. Esto se puede hacer usando

drop owned by adminuser

Si luego crea el volcado de SQL que incluye las create tabledeclaraciones ( sin la --data-onlyopción), todo debería estar bien.

También puede eliminar el --column-insertsentonces, lo que hará que la importación sea mucho más rápida.


Sin embargo, si desea eliminar todo, puede hacerlo con un poco de SQL dinámico:

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

Esto truncará todas las tablas en el esquema publiccon una sola declaración que también funcionará incluso si hay muchas restricciones de clave externa que conectan todas las tablas. Si sus tablas están distribuidas en múltiples esquemas, debe agregarlas en la wherecondición.

un caballo sin nombre
fuente
Ya veo ... ¿hace lo mismo que @ypercube mencionado anteriormente para usar este comando TRUNCATE table1, table2, ... <list of all tables>;? ¿Ambos hacen lo mismo?
uberrebu el
1
@babababa: sí, mi respuesta simplemente genera y ejecuta esa declaración dinámicamente, por lo que no tiene que escribir todos los nombres de tabla y si agrega una nueva tabla, se incluirá automáticamente.
a_horse_with_no_name
bueno, lo probé y funciona, @ypercube uno también funciona ... muchas gracias
uberrebu
6

pg_restore tiene un indicador --clean (o posiblemente --create) que eliminará automáticamente los datos antes de ejecutar operaciones

La excelente documentación debería ayudarte mucho ...

Solo para aclarar, en caso de que sea confuso:

Limpie (suelte) los objetos de la base de datos antes de recrearlos. (A menos que se use --if-exist, esto podría generar algunos mensajes de error inofensivos, si algún objeto no estuviera presente en la base de datos de destino).

Esto no eliminará la base de datos real ... solo las tablas / vistas / etc.

Si, por alguna razón, no es aceptable soltar y volver a crear las tablas, entonces tendrá que esforzarse más para crear manualmente un script que cree un data onlyvolcado de la base de datos de origen, problemas TRUNCATEo DELETEen la base de datos de destino, y luego carga el volcado de datos. No hay una forma rápida / hábil de hacer esto, que yo sepa.

Joishi Bodio
fuente
¿ese indicador de limpieza SOLO eliminará datos y mantendrá las estructuras de bases de datos y tablas iguales pero vacías?
uberrebu el
Emitirá una tabla desplegable antes de crear una tabla. Cualquier tabla que exista en el archivo de volcado. ESPERO que el archivo de volcado contenga la información para recrear la tabla exactamente como existía antes (incluyendo FKeys, etc.). Pero realmente depende de cómo haya creado el archivo de volcado. Sin embargo, dado que sigue mencionando "puesta en escena", parece que lo que realmente está buscando es una forma de llenar tablas de puesta en escena en un almacén de datos con datos de una base de datos de producción. Si ese es su objetivo, un archivo de volcado es probablemente el enfoque incorrecto ..
Joishi Bodio 02 de
eso no es lo que estoy buscando hacer, solo quiero eliminar datos ... la base de datos y la estructura de tablas permanecerán igual e intactas ... mi pregunta es bastante clara sobre lo que quiero hacer, incluso desde el título
uberrebu
Entonces, lamento decirlo, su solución será mucho más difícil.
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 consulta anterior generará consultas truncadas para todas las tablas en la base de datos.

Thirumal
fuente
0

Nota: mi respuesta se trata de eliminar realmente las tablas y otros objetos de la base de datos; para eliminar todos los datos en las tablas, es decir, truncar todas las tablas , Endre Both ha proporcionado una declaración similarmente bien ejecutada (ejecución directa) un mes después.

Para los casos en los que no puede simplemente DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;o algo así, aquí hay un script SQL independiente que escribí, que es seguro para las transacciones (es decir, puede ponerlo entre BEGIN;y ROLLBACK;para probarlo o COMMIT;hacer el acto) limpia "todos" los objetos de la base de datos ... bueno, todos los que se usan en la base de datos que usa nuestra aplicación o podría agregar sensatamente, que es:

  • disparadores en las mesas
  • restricciones en las tablas (FK, PK, CHECK, UNIQUE)
  • índices
  • VIEWs (normal o materializado)
  • mesas
  • secuencias
  • rutinas (funciones agregadas, funciones, procedimientos)
  • todos publiclos esquemas no predeterminados (es decir, no o internos de la base de datos) que "nosotros" poseemos: el script es útil cuando se ejecuta como "no un superusuario de base de datos"; un superusuario puede descartar todos los esquemas (sin embargo, los realmente importantes aún se excluyen explícitamente)
  • extensiones (aportadas por el usuario pero normalmente las dejo deliberadamente)

No se descartan (algunos deliberados; otros solo porque no tenía ningún ejemplo en nuestra base de datos):

  • el publicesquema (p. ej. para cosas proporcionadas por la extensión en ellos)
  • intercalaciones y otras cosas locales
  • disparadores de eventos
  • cosas de búsqueda de texto, ... (vea aquí para otras cosas que podría haber perdido)
  • roles u otras configuraciones de seguridad
  • tipos compuestos
  • mesas tostadas
  • FDW y tablas extranjeras

Esto es realmente útil para los casos en que el volcado que desea restaurar es de una versión de esquema de base de datos diferente (por ejemplo, con Debian dbconfig-common, Flyway o Liquibase / DB-Manul) que la base de datos en la que desea restaurarlo.

También tengo una versión que elimina "todo excepto dos tablas y lo que les pertenece" (una secuencia, probada manualmente, lo siento, lo sé, aburrida) en caso de que alguien esté interesado; La diferencia es pequeña. Contáctame o consulta este repositorio si estás interesado.

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

Probado, excepto adiciones posteriores ( extensionscontribuido por Clément Prévost ), en PostgreSQL 9.6 ( jessie-backports). Eliminación agregada probada en 9.6 y 12.2, eliminación de procedimiento probada también en 12.2. ¡Correcciones de errores y mejoras adicionales son bienvenidas!

mirabilos
fuente
Perfecto, aquí está mi código para las extensiones, debe colocarse antes de las funciones / procedimientos: - extensiones FOR r IN (SELECCIONE pns.nspname, pe.extname FROM pg_extension pe, pg_namespace pns DONDE pns.oid = pe.extnamespace Y pns .nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')) Formato LOOP EXECUTE ('DROP EXTENSION% I;', r.extname); END LOOP;
Clément Prévost
@ ClémentPrévost gracias, fusioné su comentario en el código (espero haberlo hecho bien, los comentarios carecen de formato, por favor revise). Sin embargo, normalmente dejo las extensiones sin eliminar deliberadamente (mi caso de uso se está restaurando a partir de copias de seguridad con diferentes versiones de esquema, y ​​normalmente tengo exactamente una extensión, PL / pgSQL, cargada). Sin embargo, puede ser útil para algunos, así que, ¡gracias!
mirabilos
Perfecto, gracias :)
Clément Prévost