Усечение всех таблиц в базе данных Postgres

155

Мне регулярно нужно удалять все данные из моей базы данных PostgreSQL перед перестройкой. Как бы я сделал это прямо в SQL?

На данный момент мне удалось придумать оператор SQL, который возвращает все команды, которые мне нужно выполнить:

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

Но я не вижу способа выполнить их программно, как только они у меня есть.

Sig
источник

Ответы:

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');
Henning
источник
1
Пришлось немного перенастроить, но после этого это сработало как шарм! Я никогда не использовал plpgsql раньше, так что это заняло бы у меня много времени. Спасибо! Для тех, кому это нужно, я добавил код, который я использовал в конце этого поста.
Sig
Извините, я, вероятно, думал в Oracle PL / SQL :( Я исправил синтаксическую ошибку в моем коде выше.
Henning
1
Вы также можете переместить инструкцию SELECT непосредственно в цикл FOR. DECLARE r RECORD;тогда для цикла: FOR r IN SELECT tablename FROM pg_tables LOOP
Майкл Буэн
6
Я бы добавил CASCADE в TRUNCATE TABLE
Богдан Гусиев
3
О, МОЙ БОГ!! Я просто обрезал все свои таблицы в "публичной" схеме .... пожалуйста, добавьте еще один параметр "схемы", чтобы функция усекала таблицы только в предоставленной схеме!
roneo
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_class, pg_tablesи information_schema.tables:

О regclassи цитируемые имена таблиц:

Для многократного использования

Создайте «шаблонную» базу данных (назовем ее my_template) с вашей ванильной структурой и всеми пустыми таблицами. Затем пройдите DROP/CREATE DATABASE цикл:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

Это очень быстро , потому что Postgres копирует всю структуру на уровне файлов. Никаких проблем с параллелизмом или других накладных расходов, замедляющих вас.

Если одновременные соединения не дают вам сбросить БД, подумайте:

Эрвин Брандштеттер
источник
1
Стоит отметить, что эта последняя функция стерла ВСЕ базы данных. Не только подключенный в настоящее время ... да ... назовите меня наивным, но это действительно не было ясно из этого поста.
Амальговинус
@Amalgovinus: Какая последняя функция? Ни одна из функций в моем ответе не затрагивает ничего, кроме текущей базы данных (кроме DROP DATABASE mydb, очевидно,). Может быть, вы путаете схемы с базами данных?
Эрвин Брандштеттер,
3
@Amalgovinus: Нет, это невозможно. Команда DO(как и любой другой оператор SQL) выполняется исключительно в текущей базе данных . Postgres не имеет доступа к другим базам данных в той же транзакции. Вы должны будете использовать dblink или FDW, чтобы сделать это. Но это действительно влияет на все схемы в текущей базе данных - если не добавить , WHERE t.schemaname = 'public'чтобы ограничить влияние на одной конкретной схемы в данном конкретном случае.
Эрвин Брандштеттер,
1
Очень приятно знать об этих шаблонах. Это может быть полезно даже в сценариях автоматизированных тестов, где может потребоваться сброс / подготовка базы данных.
hbobenicio
3
Спасибо за отличный ответ, я использую «Одиночная команда, без цикла», которая возвращает команду TRUNCATE, как мне выполнить ее?
Mahyar
40

Если мне нужно сделать это, я просто создам схему sql текущего db, затем перетащу и создаю db, затем загрузлю db со схемой sql.

Ниже приведены этапы:

1) Создать дамп схемы базы данных ( --schema-only)

pg_dump mydb -s > schema.sql

2) Удалить базу данных

drop database mydb;

3) Создать базу данных

create database mydb;

4) Схема импорта

psql mydb < schema.sql

Sandip Ransing
источник
9

В этом случае, вероятно, было бы лучше иметь пустую базу данных, которую вы используете в качестве шаблона, а когда вам нужно обновить, удалите существующую базу данных и создайте новую из шаблона.

Скотт Бэйли
источник
3

Не могли бы вы использовать динамический SQL для выполнения каждого оператора по очереди? Возможно, вам придется написать скрипт PL / pgSQL для этого.

http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html (раздел 38.5.4. Выполнение динамических команд)

FrustratedWithFormsDesigner
источник
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;
RomanGorbatko
источник
3

Ребята, лучший и чистый способ:

1) Создать дамп схемы базы данных (только для --schema) pg_dump mydb -s> schema.sql

2) Удалить базу данных, удалить базу данных mydb;

3) Создать базу данных создать базу данных mydb;

4) Импортировать схему psql mydb <schema.sql

Это работа для меня!

Хорошего дня. Хирам Уокер

Хирам Уокер
источник
2

Если вы можете использовать psql, вы можете использовать \gexecкоманду meta для выполнения вывода запроса;

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 вы можете сделать:

  • Щелкните правой кнопкой мыши базу данных -> резервное копирование, выберите «Только схема»
  • Удалить базу данных
  • Создайте новую базу данных и назовите ее как прежнюю
  • Щелкните правой кнопкой мыши новую базу данных -> восстановить -> выберите резервную копию, выберите «Только схема»
mYnDstrEAm
источник