【问题标题】:Truncating all tables in a Postgres database截断 Postgres 数据库中的所有表
【发布时间】:2011-02-19 05:25:14
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: postgresql plpgsql dynamic-sql truncate database-table


    【解决方案1】:

    能否使用动态 SQL 依次执行每条语句?您可能必须编写一个 PL/pgSQL 脚本来执行此操作。

    http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html(第 38.5.4 节。执行动态命令)

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 不得不重新调整一下,但之后它就像一个魅力!我以前从未使用过 plpgsql,所以这会花费我很多时间。谢谢!对于任何需要它的人,我已将我最终使用的代码添加到这篇文章的底部。
      • 您也可以将 SELECT 语句直接移动到 FOR 循环。 DECLARE r RECORD; 然后 for 循环:FOR r IN SELECT tablename FROM pg_tables LOOP
      • 我会将 CASCADE 添加到 TRUNCATE TABLE
      • 天啊!!我刚刚截断了“公共”模式中的所有表......请添加另一个“模式”参数,以便该函数仅在提供的模式上截断表!
      • @roneo 查看问题:ALL TABLES 在数据库中!
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        注意:由于每个数据库的表名不是唯一的,因此您必须对表名进行模式限定以确保。此外,我将功能限制为默认模式“公共”。适应您的需求,但请务必排除系统架构 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  
              -- 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$ 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
           -- 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$ LANGUAGE plpgsql;
        

        呼叫:

        SELECT truncate_tables('postgres');
        

        细化查询

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

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

        关于pg_classpg_tablesinformation_schema.tables的区别:

        关于regclass 和引用的表名:

        重复使用

        使用您的原版结构和所有空表创建一个“模板”数据库(我们将其命名为my_template)。然后经过一个DROP/CREATE DATABASE循环:

        DROP DATABASE mydb;
        CREATE DATABASE mydb TEMPLATE my_template;

        这是非常,因为 Postgres 在文件级别复制整个结构。没有并发问题或其他开销减慢您的速度。

        如果并发连接阻止您删除数据库,请考虑:

        【讨论】:

        • 值得注意的是,最后一个函数擦除了所有数据库。不仅仅是当前连接的那个......是的......称我为天真,但这篇文章真的不清楚。
        • @Amalgovinus:最后一个函数是什么?我的答案中没有任何功能涉及 current 数据库之外的任何内容(显然,DROP DATABASE mydb 除外)。您是否将 模式 与数据库混淆了?
        • @Amalgovinus:不,那是不可能的。 DO 命令(与任何其他 SQL 语句一样)在当前数据库中独占执行。 Postgres 无法在同一个事务中访问其他数据库。您必须使用 dblink 或 FDW 来执行此操作。但它确实影响当前数据库中的所有模式 - 除非您添加 WHERE t.schemaname = 'public' 以在这种特殊情况下将效果限制为一个特定模式。
        • 很高兴了解这些模板。即使在可能需要数据库重置/准备的自动化测试场景中,这也很有用。
        • 感谢您的出色回答,我正在使用“单个命令,无循环”返回 TRUNCATE 命令,我应该如何执行它?
        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          如果我必须这样做,我将简单地创建当前 db 的模式 sql,然后删除并创建 db,然后使用模式 sql 加载 db。

          以下是涉及的步骤:

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

          pg_dump mydb -s > schema.sql

          2) 删除数据库

          drop database mydb;

          3) 创建数据库

          create database mydb;

          4) 导入架构

          psql mydb < schema.sql

          【讨论】:

            【解决方案7】:

            要删除数据并保留pgAdmin 中的表结构,您可以这样做:

            • 右键数据库->备份,选择“Schema only”
            • 删除数据库
            • 创建一个新数据库并像以前一样命名
            • 右键新建数据库->还原->选择备份,选择“仅架构”

            【讨论】:

              【解决方案8】:

              清理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;
              

              【讨论】:

                【解决方案9】:

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

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

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

                3) 创建数据库 创建数据库mydb;

                4) 导入架构 psql mydb

                这对我有用!

                祝你有美好的一天。 海勒姆·沃克

                【讨论】:

                  【解决方案10】:

                  如果可以使用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版本中引入的

                  【讨论】:

                    【解决方案11】:

                    只需执行下面的查询:

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

                    【讨论】:

                    • 我尝试了许多其他解决方案,但只有一个有效。
                    猜你喜欢
                    • 2011-08-12
                    • 1970-01-01
                    • 2011-08-27
                    • 1970-01-01
                    • 2011-06-12
                    • 2015-09-19
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-03-13
                    相关资源
                    最近更新 更多