Postgres:在从 bash 脚本重新创建/重新填充之前清除整个数据库
Posted
技术标签:
【中文标题】Postgres:在从 bash 脚本重新创建/重新填充之前清除整个数据库【英文标题】:Postgres: clear entire database before re-creating / re-populating from bash script 【发布时间】:2011-01-04 15:21:15 【问题描述】:我正在编写一个 shell 脚本(将成为一个 cronjob),它将:
1:转储我的生产数据库
2:将转储导入我的开发数据库
在第 1 步和第 2 步之间,我需要清除开发数据库(删除所有表?)。这如何最好地从 shell 脚本中完成?到目前为止,它看起来像这样:
#!/bin/bash
time=`date '+%Y'-'%m'-'%d'`
# 1. export(dump) the current production database
pg_dump -U production_db_name > /backup/dir/backup-$time.sql
# missing step: drop all tables from development database so it can be re-populated
# 2. load the backup into the development database
psql -U development_db_name < backup/dir/backup-$time.sql
【问题讨论】:
oneliner 为赶时间的人:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
这个 oneliner 要求您拥有创建/删除数据库的权限。作者尝试的方法不需要特殊权限。
【参考方案1】:
虽然下面这行取自 Windows 批处理脚本,但命令应该非常相似:
psql -U username -h localhost -d postgres -c "DROP DATABASE \"$DATABASE\";"
此命令用于通过实际删除整个数据库来清除它。命令中的$DATABASE
(在Windows 中应该是%DATABASE%
)是一个windows 风格的环境变量,其计算结果为数据库名称。您需要将其替换为您的 development_db_name
。
【讨论】:
那么为什么不使用已经可用的dropdb
和createdb
命令呢?如果你可以运行 psql,你也可以运行它们。【参考方案2】:
我只是删除数据库然后重新创建它。在 UNIX 或 Linux 系统上,应该这样做:
$ dropdb development_db_name
$ createdb development_db_name
其实我就是这么干的。
【讨论】:
这也是我的做法。然后只需恢复到新创建的数据库。 是的。这更好,因为可能存在不属于您正在恢复的转储的对象。在这种情况下,他们肯定会被杀死。 一个节省我时间的技巧是 $ sudo -u postgres dropdb DATABASE_NAME 但是……数据库权限和所有权呢? @EmanuelePaolinicreatedb --owner=db_owner [--template=template0 --encoding=UTF8] db_name
我默认将最后两个添加到所有数据库中【参考方案3】:
如果您实际上不需要备份以纯文本 .sql 脚本文件格式转储到磁盘上的数据库,您可以通过管道将 pg_dump
和 pg_restore
直接连接在一起。
要删除和重新创建表,您可以使用pg_dump
的--clean
命令行选项发出SQL 命令以在创建数据库对象之前清理(删除)数据库对象。 (这不会删除整个数据库,只会删除每个表/序列/索引/等,然后再重新创建它们。)
上面的两个看起来像这样:
pg_dump -U username --clean | pg_restore -U username
【讨论】:
我喜欢这个解决方案,因为我确实想要一个备份副本,所以我现在这样做:pg_dump -Ft -U production_db_name > /backup/dir/backup-$time.tar pg_restore - U development_db_name -d development_db_name -O --clean /backup/dir/backup-$time.tar 就像一个魅力,感谢您的帮助! 注意:--clean 选项只会删除那些在恢复文件中找到的关系。这意味着,如果您添加一个表进行测试,然后想要删除它(例如与生产数据库同步),它将不会被删除。 重要的是要记住 pg_dump 的 --clean 选项仅适用于纯文本备份。正如文档在postgresql.org/docs/9.4/static/app-pgdump.html 中明确指出的那样,您需要在 pg_restore 上使用 --clean 进行存档备份。 有没有办法在“--clean”选项中包含级联。因为它是这个选项看起来没用。我收到“错误:无法公开模式,因为其他对象依赖它”,就像 100% 的时间使用它一样。 关于删除所有表的问题。这只会删除在 pg_dump 正在转储的数据库中找到的表。【参考方案4】:我用过:
pg_restore -c -d database_name filename.dump
【讨论】:
【参考方案5】:转储:
pg_dump -Fc mydb > db.dump
恢复:
pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d my_db db/latest.dump
【讨论】:
【参考方案6】:注意:我的回答是关于真正删除表和其他数据库对象;对于deleting all data in the tables, i.e. truncating all tables,Endre Both 在一个月后提供了同样执行良好(直接执行)的语句。
对于不能只使用DROP SCHEMA public CASCADE;
、DROP OWNED BY current_user;
或其他东西的情况,这是我编写的独立 SQL 脚本,它是事务安全的(即您可以将它放在 BEGIN;
和 @ 987654328@ 只是测试它或COMMIT;
实际执行)并清理“所有”数据库对象......好吧,我们的应用程序使用的数据库中使用的所有对象,或者我可以明智地添加,即:
CHECK
、UNIQUE
)
指标
VIEW
s(正常或具体化)
表格
序列
例程(聚合函数、函数、过程)
所有非默认(即不是public
或DB-internal)模式“我们”拥有:该脚本在以“非数据库超级用户”身份运行时很有用;超级用户可以删除 所有 模式(不过,真正重要的模式仍然被明确排除)
扩展(用户提供,但我通常故意将它们留在里面)
没有删除是(有些是故意的;有些只是因为我在我们的数据库中没有示例):
public
架构(例如,用于其中的扩展提供的东西)
排序规则和其他语言环境的东西
事件触发器
文本搜索内容,...(请参阅 here 了解我可能错过的其他内容)
角色或其他安全设置
复合类型
烤面包桌
FDW 和外部表
当您要恢复的转储与数据库具有不同的数据库架构版本(例如,使用 Debian dbconfig-common
、Flyway 或 Liquibase/DB-Manul)时,这真的非常有用你想把它恢复到。
我还有一个版本,它删除了“除了两个表和属于它们的所有内容”(一个序列,手动测试,抱歉,我知道,无聊)以防有人感兴趣;差异很小。有兴趣请联系我或check this repo。
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; $$;
在 PostgreSQL 9.6 (jessie-backports
) 上经过测试,除了后来添加的内容(Clément Prévost 贡献的extensions
)。在 9.6 和 12.2 上测试了骨料去除,也在 12.2 上测试了过程去除。欢迎进行错误修复和进一步改进!
【讨论】:
【参考方案7】:如果要清理名为“example_db”的数据库:
1) 登录另一个数据库(例如'postgres'):
psql postgres
2) 删除您的数据库:
DROP DATABASE example_db;
3) 重新创建您的数据库:
CREATE DATABASE example_db;
【讨论】:
以上是关于Postgres:在从 bash 脚本重新创建/重新填充之前清除整个数据库的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Docker Postgres 的脚本中创建用户/数据库
为啥在从 C# 创建的进程中运行 bash 命令时我的 $PATH 不同?
Json String Parsing 在从 MSDOS 运行时有效,但在 Windows 上的 Ubuntu 上的 Bash 中无效 [重复]