我经常需要在重建之前删除PostgreSQL数据库中的所有数据。如何在SQL中直接做到这一点?
目前,我已经设法想出了一个SQL语句,返回我需要执行的所有命令:
SELECT 'TRUNCATE TABLE ' || tablename || ';' FROM pg_tables WHERE tableowner='MYUSER';
但是,一旦我有了它们,我就看不到以编程方式执行它们的方法。
我经常需要在重建之前删除PostgreSQL数据库中的所有数据。如何在SQL中直接做到这一点?
目前,我已经设法想出了一个SQL语句,返回我需要执行的所有命令:
SELECT 'TRUNCATE TABLE ' || tablename || ';' FROM pg_tables WHERE tableowner='MYUSER';
但是,一旦我有了它们,我就看不到以编程方式执行它们的方法。
当前回答
如果你会使用psql,你可以使用\gexec元命令来执行查询输出;
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
注意,\gexec是在9.6版本中引入的
其他回答
您可以使用类似的方法来获取所有截断查询。
SELECT 'TRUNCATE TABLE ' || table_name || ';'
FROM information_schema.tables
WHERE table_schema='schema_name'
AND table_type='BASE TABLE';
您可以使用动态SQL依次执行每条语句吗?您可能必须编写一个PL/pgSQL脚本来完成此操作。
http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html(第38.5.4节)。执行动态命令)
在PL/pgSQL中很少需要显式游标。使用FOR循环中更简单更快的隐式游标:
由于表名在每个数据库中不是唯一的,因此必须对表名进行模式限定以确保这一点。此外,我将该函数限制为默认模式“public”。根据您的需要进行调整,但一定要排除系统模式pg_*和information_schema。
使用这些函数时要非常小心。他们破坏你的数据库。我加了一个儿童安全装置。注释RAISE NOTICE行和取消注释EXECUTE来启动炸弹…
CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
RETURNS void
LANGUAGE plpgsql 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
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
format('TRUNCATE TABLE %I.%I CASCADE', _sch, _tbl);
END LOOP;
END
$func$;
format()要求Postgres 9.1或更高版本。在旧版本中,像这样连接查询字符串:
'TRUNCATE TABLE ' || quote_ident(_sch) || '.' || quote_ident(_tbl) || ' CASCADE';
单个命令,没有循环
因为我们可以一次截断多个表,所以我们根本不需要任何游标或循环:
在数组中传递表名
聚合所有表名并执行一条语句。更简单、更快:
CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
(SELECT 'TRUNCATE TABLE '
|| string_agg(format('%I.%I', schemaname, tablename), ', ')
|| ' CASCADE'
FROM pg_tables
WHERE tableowner = _username
AND schemaname = 'public'
);
END
$func$;
电话:
SELECT truncate_tables('postgres');
精确查询
你甚至不需要一个函数。在Postgres 9.0+中,您可以在DO语句中执行动态命令。在Postgres 9.5+中,语法可以更简单:
DO
$do$
BEGIN
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
(SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
FROM pg_class
WHERE relkind = 'r' -- only tables
AND relnamespace = 'public'::regnamespace
);
END
$do$;
关于pg_class, pg_tables和information_schema.tables之间的区别:
如何检查一个表是否存在于给定的模式中
关于regclass和引用表名:
表名作为PostgreSQL函数参数
重复使用
创建一个“模板”数据库(让我们命名为my_template),使用您的普通结构和所有空表。然后通过DROP / CREATE DATABASE循环:
DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;
这非常快,因为Postgres在文件级别复制了整个结构。没有并发问题或其他开销减慢您的速度。
如果并发连接阻止你删除数据库,考虑:
强制删除数据库,而其他数据库可能已连接
挫折withformsdesigner是正确的,PL/pgSQL可以这样做。剧本如下:
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;
这将创建一个存储函数(你只需要这样做一次),之后你可以像这样使用:
SELECT truncate_tables('MYUSER');
如果我必须这样做,我将简单地创建当前db的schema sql,然后删除并创建db,然后用schema sql加载db。
以下是所涉及的步骤:
1)创建数据库的模式转储(——Schema -only)
Pg_dump mydb -s > schema.sql
2)删除数据库
删除mydb数据库;
3)创建数据库
创建mydb数据库
4)导入模式
PSQL myDB < schema.sql