我经常需要在重建之前删除PostgreSQL数据库中的所有数据。如何在SQL中直接做到这一点?

目前,我已经设法想出了一个SQL语句,返回我需要执行的所有命令:

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

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


当前回答

简单地说,你可以运行下面这段SQL:

DO $$ DECLARE
  r RECORD;
BEGIN
  FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname =current_schema()) LOOP
    EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE';
  END LOOP;
END $$;

其他回答

只需执行下面的查询:

DO $$ DECLARE
    r RECORD;
BEGIN
    FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
        EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || '';
    END LOOP;
END $$;

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

你也可以用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 "{}"

您需要调整模式名称、密码和用户名以匹配您的模式。

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