截断Postgres数据库中的所有表


155

重建之前,我通常需要从PostgreSQL数据库中删除所有数据。我将如何直接在SQL中执行此操作?

目前,我设法提出了一条SQL语句,该语句返回我需要执行的所有命令:

SELECT 'TRUNCATE TABLE ' ||  tablename || ';' FROM pg_tables WHERE tableowner='MYUSER';

但是,一旦有了它们,我就看不到以编程方式执行它们的方法。

Answers:


226

FrustratedWithFormsDesigner是正确的,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');

1
不得不重新调整一下,但之后它就像一个魅力!我以前从未使用过plpgsql,所以这花了我很长时间。谢谢!对于需要它的任何人,我已经将我最终使用的代码添加到了本文的底部。
Sig 2010年

对不起,我可能在甲骨文的PL / SQL思维:(我,在我的代码中的语法错误之上。
亨宁

1
您还可以将SELECT语句直接移至FOR循环。 DECLARE r RECORD;然后循环: FOR r IN SELECT tablename FROM pg_tables LOOP
Michael Buen

6
我会将CASCADE添加到TRUNCATE TABLE
Bogdan Gusiev

3
我的天啊!!我只是截断了“公共”模式中的所有表。...请添加另一个“ schema”参数,以便函数仅在提供的模式中截断表!
roneo '17

95

在plpgsql中很少需要显式游标。使用更简单,更快的循环隐式游标FOR

注意:由于表名在每个数据库中都不唯一,因此必须对表名进行模式限定。另外,我将功能限制为默认模式“ public”。适应您的需求,但请确保排除系统架构pg_*information_schema

这些功能要非常小心。他们破坏您的数据库。我添加了儿童安全装置。评论RAISE NOTICE界线并取消评论EXECUTE以准备炸弹...

CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
  RETURNS void 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
      RAISE NOTICE '%',
      -- EXECUTE  -- dangerous, test before you execute!
         format('TRUNCATE TABLE %I.%I CASCADE', _sch, _tbl);
   END LOOP;
END
$func$ LANGUAGE plpgsql;

format()需要Postgres 9.1或更高版本。在旧版本中,将查询字符串连接起来,如下所示:

'TRUNCATE TABLE ' || quote_ident(_sch) || '.' || quote_ident(_tbl)  || ' CASCADE';

单个命令,无循环

由于我们可以TRUNCATE一次创建多个表,因此根本不需要任何游标或循环:

汇总所有表名并执行一条语句。更简单,更快:

CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
  RETURNS void AS
$func$
BEGIN
   RAISE NOTICE '%', 
   -- EXECUTE  -- dangerous, test before you execute!
  (SELECT 'TRUNCATE TABLE '
       || string_agg(format('%I.%I', schemaname, tablename), ', ')
       || ' CASCADE'
   FROM   pg_tables
   WHERE  tableowner = _username
   AND    schemaname = 'public'
   );
END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT truncate_tables('postgres');

细化查询

您甚至不需要功能。在Postgres 9.0+中,您可以在DO语句中执行动态命令。在Postgres 9.5+中,语法可以更简单:

DO
$func$
BEGIN
   RAISE NOTICE '%', 
   -- EXECUTE
   (SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
    FROM   pg_class
    WHERE  relkind = 'r'  -- only tables
    AND    relnamespace = 'public'::regnamespace
   );
END
$func$;

关于之间的差异pg_classpg_tables以及information_schema.tables

关于regclass和引用表名:

重复使用

使用您的香草结构和所有空表创建一个“模板”数据库(命名为my_template)。然后经历一个DROP/CREATE DATABASE循环:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

非常,因为Postgres在文件级别复制整个结构。没有并发问题或其他开销使您慢下来。

如果并发连接使您无法删除数据库,请考虑:


1
值得注意的是,该最后一个函数擦除了所有数据库。不仅是当前连接的人……是的,我叫天真,但这在这篇文章中还不清楚。
Amalgovinus 2015年

@Amalgovinus:最后一个功能是什么?我的答案中的任何功能都没有触及当前数据库之外的任何内容(DROP DATABASE mydb显然,除外)。您是否将架构与数据库混淆了?
Erwin Brandstetter 2015年

3
@Amalgovinus:不,那是不可能的。该DO指令(像任何其他SQL语句)在当前数据库中执行独占。Postgres无法在同一事务中访问其他数据库。您必须使用dblink或FDW来执行此操作。但这确实会影响当前数据库中的所有架构-除非您添加WHERE t.schemaname = 'public'以在这种特定情况下将效果限制为一个特定架构。
Erwin Brandstetter

1
非常了解这些模板。即使在可能需要数据库重置/准备的自动测试场景中,这也可能很有用。
hbobenicio

3
感谢您的出色回答,我使用的是“单命令,无循环”,该命令返回TRUNCATE命令,应该如何执行呢?
Mahyar

40

如果必须执行此操作,则只需创建当前数据库的模式sql,然后删除并创建数据库,然后使用模式sql加载db。

以下是涉及的步骤:

1)创建数据库的架构转储(--schema-only

pg_dump mydb -s > schema.sql

2)删除数据库

drop database mydb;

3)创建数据库

create database mydb;

4)导入架构

psql mydb < schema.sql


9

在这种情况下,最好有一个空数据库作为模板,并在需要刷新时删除现有数据库并从模板创建一个新数据库。



3

您也可以使用bash进行此操作:

#!/bin/bash
PGPASSWORD='' psql -h 127.0.0.1 -Upostgres sng --tuples-only --command "SELECT 'TRUNCATE TABLE ' || schemaname || '.' ||  tablename || ';' FROM pg_tables WHERE schemaname in ('cms_test', 'ids_test', 'logs_test', 'sps_test');" | 
tr "\\n" " " | 
xargs -I{} psql -h 127.0.0.1 -Upostgres sng --command "{}"

您将需要调整架构名称,密码和用户名以匹配您的架构。


3

清洁AUTO_INCREMENT版本:

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

        IF EXISTS (
            SELECT column_name 
            FROM information_schema.columns 
            WHERE table_name=quote_ident(stmt.tablename) and column_name='id'
        ) THEN
           EXECUTE 'ALTER SEQUENCE ' || quote_ident(stmt.tablename) || '_id_seq RESTART WITH 1';
        END IF;

    END LOOP;
END;
$$ LANGUAGE plpgsql;

3

伙计们,更好,更干净的方法是:

1)创建数据库的模式转储(仅用于模式)pg_dump mydb -s> schema.sql

2)删除数据库删除数据库mydb;

3)Create Database创建数据库mydb;

4)导入架构psql mydb <schema.sql

为我工作!

祝你今天愉快。希拉姆·沃克


2

如果可以使用psql,则可以使用\gexecmeta命令执行查询输出。

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版


1

要在pgAdmin中删除数据并保留表结构,您可以执行以下操作:

  • 右键单击数据库->备份,选择“仅模式”
  • 删除数据库
  • 创建一个新数据库,并像以前一样命名
  • 右键单击新数据库->恢复->选择备份,选择“仅模式”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.