Обрізання всіх таблиць у базі даних Postgres


155

Мені регулярно потрібно видаляти всі дані з моєї бази даних PostgreSQL перед відновленням. Як би я це зробив безпосередньо в SQL?

На даний момент мені вдалося придумати оператор SQL, який повертає всі команди, необхідні для виконання:

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

Але я не бачу способу їх програмного виконання, як тільки я їх маю.

Відповіді:


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 раніше, тому це зайняло б у мене віки. Дякую! Для всіх, хто потребує цього, я додав код, який я в кінцевому підсумку використав у нижній частині цієї публікації.
Зіг

Вибачте, я, мабуть, думав в Oracle PL / SQL :( я виправив синтаксичну помилку в своєму коді вище.
Геннінг

1
Ви також можете перемістити оператор SELECT безпосередньо до циклу FOR. DECLARE r RECORD;потім для циклу: FOR r IN SELECT tablename FROM pg_tables LOOP
Майкл Буен

6
Я додав би CASCADE до ТРУНКАЦІЙНОГО СТОЛУ
Богдан Гусєв

3
О БОЖЕ МІЙ!! Я просто обрізав всі мої таблиці в "загальнодоступній" схемі .... pls додайте ще один параметр "schema", щоб функція усікала таблиці тільки на схемі, яка надається!
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, очевидно,). Ви плутаєте схеми з базами даних, можливо?
Erwin Brandstetter

3
@Amalgovinus: Ні, це неможливо. DOКоманда (як і будь-який інший інструкції SQL) виконується в поточній базі даних виключно . Postgres не має можливості отримати доступ до інших баз даних за тією ж транзакцією. Для цього вам доведеться використовувати dblink або FDW. Але це дійсно впливає на всі схеми в поточній базі даних - якщо не додати , WHERE t.schemaname = 'public'щоб обмежити вплив на одній конкретної схеми в даному конкретному випадку.
Ервін Брандстеттер

1
Дійсно приємно знати про ці шаблони. Це може бути корисним навіть у сценаріях автоматизованих тестів, де може знадобитися скидання / підготовка бази даних.
hbobenicio

3
Дякую за чудову відповідь, я використовую "Одинична команда, без циклу", яка повертає команду TRUNCATE, як мені слід йти про її виконання?
Махяр

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


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) Створити базу даних створити базу даних mydb;

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

Це працює для мене!

Гарного дня. Хірам Уокер


2

Якщо ви можете використовувати 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


1

Для видалення даних та збереження структур таблиць у pgAdmin ви можете:

  • Клацніть правою кнопкою миші базу даних -> резервна копія, виберіть "Тільки схема"
  • Відкиньте базу даних
  • Створіть нову базу даних і назвіть її як попередню
  • Клацніть правою кнопкою миші нову базу даних -> відновити -> виберіть резервну копію, виберіть "Тільки схема"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.