การตัดทอนตารางทั้งหมดในฐานข้อมูล 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 มาก่อนดังนั้นนี่จะทำให้ฉันอายุมากขึ้น ขอบคุณ! สำหรับทุกคนที่ต้องการฉันได้เพิ่มรหัสฉันลงเอยด้วยการใช้ที่ด้านล่างของโพสต์นี้
Sig

ขออภัยผมก็อาจจะคิดใน Oracle PL / SQL :( ฉันคงผิดพลาดทางไวยากรณ์ในรหัสของฉันข้างต้น.
เฮนนิ่ง

1
คุณยังสามารถย้ายคำสั่ง SELECT โดยตรงไปยัง FOR loop DECLARE r RECORD;สำหรับลูปแล้ว: FOR r IN SELECT tablename FROM pg_tables LOOP
Michael Buen

6
ฉันจะเพิ่มเรียงซ้อนในตารางการตัด
Bogdan Gusiev

3
พระเจ้าช่วย!! ฉันเพิ่งตัดทอนตารางของฉันทั้งหมดในสคีมา "สาธารณะ" โปรดเพิ่มพารามิเตอร์อื่นของ "สคีมา" เพื่อให้ฟังก์ชันตัดทอนตารางเฉพาะบนสคีมาที่ให้ไว้เท่านั้น
roneo

95

เคอร์เซอร์ที่ชัดเจนนั้นไม่ค่อยจำเป็นใน plpgsql ใช้เคอร์เซอร์โดยนัยที่ง่ายและเร็วกว่าของFORลูป:

หมายเหตุ:เนื่องจากชื่อตารางไม่ซ้ำกันสำหรับฐานข้อมูลคุณจึงต้องตรวจสอบชื่อตารางเพื่อให้แน่ใจ นอกจากนี้ฉัน จำกัด ฟังก์ชั่นให้เป็นสคีมาเริ่มต้น 'สาธารณะ' ปรับให้เข้ากับความต้องการของคุณ แต่ต้องแน่ใจว่าจะไม่รวม schemas ระบบและ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) กับโครงสร้างวานิลลาของคุณและตารางที่ว่างเปล่าทั้งหมด จากนั้นผ่าน a DROP/CREATE DATABASE cycle:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

สิ่งนี้เร็วมากเนื่องจาก Postgres จะคัดลอกโครงสร้างทั้งหมดในระดับไฟล์ ไม่มีปัญหาการทำงานพร้อมกันหรือค่าใช้จ่ายอื่น ๆ ทำให้คุณช้าลง

หากการเชื่อมต่อที่เกิดขึ้นพร้อมกันทำให้คุณไม่สามารถทิ้ง DB ได้ให้พิจารณา:


1
เป็นที่น่าสังเกตว่าฟังก์ชั่นสุดท้ายนี้เช็ดฐานข้อมูลทั้งหมด ไม่ใช่แค่หนึ่งที่เชื่อมต่ออยู่ในปัจจุบัน .... ใช่ ... โทรหาฉัน naiive แต่นั่นไม่ชัดเจนจากโพสต์นี้
Amalgovinus

@Amalgovinus: ฟังก์ชันสุดท้ายไหน? ฟังก์ชั่นใดในคำตอบของฉันไม่แตะสิ่งใดนอกฐานข้อมูลปัจจุบัน (ยกเว้นDROP DATABASE mydbชัดเจน) คุณสับสนสกีมากับฐานข้อมูลหรือเปล่า?
Erwin Brandstetter

3
@ Amalgovinus: ไม่นั่นเป็นไปไม่ได้ DOคำสั่ง (เช่นคำสั่ง SQL อื่น ๆ ) จะถูกดำเนินการในฐานข้อมูลปัจจุบันเฉพาะ Postgres ไม่มีวิธีการเข้าถึงฐานข้อมูลอื่น ๆ ในการทำธุรกรรมเดียวกัน คุณจะต้องใช้ dblink หรือ FDW เพื่อทำเช่นนั้น แต่จะมีผลกับ schema ทั้งหมดในฐานข้อมูลปัจจุบัน - ยกเว้นว่าคุณเพิ่มWHERE t.schemaname = 'public'เพื่อ จำกัด ผลกระทบกับ schema หนึ่งโดยเฉพาะในกรณีนี้โดยเฉพาะ
Erwin Brandstetter

1
ดีมากที่รู้เกี่ยวกับเทมเพลตเหล่านั้น สิ่งนี้มีประโยชน์สำหรับฉันแม้ในสถานการณ์การทดสอบอัตโนมัติซึ่งอาจจำเป็นต้องมีการรีเซ็ต / เตรียมฐานข้อมูล
hbobenicio

3
ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมฉันใช้ "คำสั่งเดียวไม่มีวน" ซึ่งส่งกลับคำสั่ง TRUNCATE ฉันควรดำเนินการอย่างไร
Mahyar

40

หากฉันต้องทำสิ่งนี้ฉันจะสร้าง schema sql ของ db ปัจจุบันจากนั้นปล่อย & สร้าง db จากนั้นโหลด db ด้วย schema sql

ด้านล่างนี้เป็นขั้นตอนที่เกี่ยวข้อง:

1) สร้าง Schema dump ของฐานข้อมูล ( --schema-only)

pg_dump mydb -s > schema.sql

2) วางฐานข้อมูล

drop database mydb;

3) สร้างฐานข้อมูล

create database mydb;

4) Schema นำเข้า

psql mydb < schema.sql


9

ในกรณีนี้อาจเป็นการดีกว่าถ้าคุณมีฐานข้อมูลเปล่าที่คุณใช้เป็นแม่แบบและเมื่อคุณต้องการรีเฟรชให้ปล่อยฐานข้อมูลที่มีอยู่แล้วสร้างใหม่จากแม่แบบ


3

คุณสามารถใช้ไดนามิก SQL เพื่อดำเนินการแต่ละคำสั่งได้หรือไม่? คุณอาจจะต้องเขียนสคริปต์ PL / pgSQL เพื่อทำสิ่งนี้

http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html (ส่วน 38.5.4. การเรียกใช้คำสั่งแบบไดนามิก)


3

คุณสามารถทำได้ด้วยการทุบตีด้วย:

#!/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คำสั่ง 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คุณสามารถทำได้:

  • คลิกขวาที่ฐานข้อมูล -> สำรองข้อมูลเลือก "Schema only"
  • วางฐานข้อมูล
  • สร้างฐานข้อมูลใหม่และตั้งชื่อเหมือนเดิม
  • คลิกขวาที่ฐานข้อมูลใหม่ -> กู้คืน -> เลือกการสำรองข้อมูลเลือก "Schema only"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.