Cắt bớt tất cả các bảng trong cơ sở dữ liệu Postgres


155

Tôi thường xuyên cần xóa tất cả dữ liệu khỏi cơ sở dữ liệu PostgreSQL của mình trước khi xây dựng lại. Làm thế nào tôi có thể làm điều này trực tiếp trong SQL?

Hiện tại tôi đã quản lý để đưa ra một câu lệnh SQL trả về tất cả các lệnh tôi cần thực thi:

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

Nhưng tôi không thể thấy một cách để thực hiện chúng theo chương trình một khi tôi có chúng.

Câu trả lời:


226

FrustratedWithFormsDesigner là chính xác, PL / pgSQL có thể làm điều này. Đây là kịch bản:

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;

Điều này tạo ra một chức năng được lưu trữ (bạn cần làm điều này chỉ một lần) mà sau đó bạn có thể sử dụng như thế này:

SELECT truncate_tables('MYUSER');

1
Phải vui mừng một chút nhưng sau đó nó hoạt động như một cơ duyên! Tôi chưa bao giờ sử dụng plpgsql trước đây vì vậy điều này sẽ khiến tôi mất nhiều thời gian. Cảm ơn! Đối với bất kỳ ai cần nó, tôi đã thêm mã mà tôi đã sử dụng vào cuối bài viết này.
Sig

Xin lỗi, có lẽ tôi đã suy nghĩ trong Oracle PL / SQL :( Tôi đã sửa lỗi cú pháp trong mã của mình ở trên.
Henning

1
bạn cũng có thể di chuyển câu lệnh CHỌN trực tiếp sang vòng lặp FOR. DECLARE r RECORD;sau đó cho vòng lặp: FOR r IN SELECT tablename FROM pg_tables LOOP
Michael Buen

6
Tôi sẽ thêm CASCADE vào BẢNG TRUNCATE
Bogdan Gusiev

3
CHÚA ƠI!! Tôi vừa cắt bớt tất cả các bảng của mình trong lược đồ "công khai" .... vui lòng thêm một tham số khác của "lược đồ" để hàm cắt bớt các bảng chỉ trên lược đồ được cung cấp!
roneo

95

Con trỏ rõ ràng hiếm khi cần thiết trong plpgsql. Sử dụng con trỏ ngầm đơn giản hơn và nhanh hơn của mộtFOR vòng lặp:

Lưu ý: Vì tên bảng không phải là duy nhất cho mỗi cơ sở dữ liệu, bạn phải đảm bảo tên bảng đủ điều kiện lược đồ. Ngoài ra, tôi giới hạn hàm ở lược đồ mặc định 'công khai'. Thích ứng với nhu cầu của bạn, nhưng hãy chắc chắn loại trừ các lược đồ hệ thống pg_*information_schema.

Hãy rất cẩn thận với các chức năng này. Họ nuke cơ sở dữ liệu của bạn. Tôi đã thêm một thiết bị an toàn cho trẻ em. Nhận xét RAISE NOTICEdòng và không chú EXECUTEý đến nguyên tố bom ...

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()yêu cầu Postgres 9.1 trở lên. Trong các phiên bản cũ hơn nối chuỗi truy vấn như thế này:

'TRUNCATE TABLE ' || quote_ident(_sch) || '.' || quote_ident(_tbl)  || ' CASCADE';

Lệnh đơn, không lặp

Vì chúng ta có thể TRUNCATEnhiều bảng cùng một lúc, chúng ta không cần bất kỳ con trỏ hoặc vòng lặp nào cả:

Tổng hợp tất cả các tên bảng và thực hiện một câu lệnh. Đơn giản hơn, nhanh hơn:

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;

Gọi:

SELECT truncate_tables('postgres');

Truy vấn tinh chỉnh

Bạn thậm chí không cần một chức năng. Trong Postgres 9.0+, bạn có thể thực thi các lệnh động trong một DOcâu lệnh. Và trong Postgres 9.5+, cú pháp có thể đơn giản hơn nữa:

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

Về sự khác biệt giữa pg_class, pg_tablesinformation_schema.tables:

Giới thiệu regclassvà trích dẫn tên bảng:

Để sử dụng nhiều lần

Tạo cơ sở dữ liệu "mẫu" (đặt tên cho nó my_template) với cấu trúc vanilla của bạn và tất cả các bảng trống. Sau đó trải qua một DROP/CREATE DATABASE chu kỳ:

DROP DATABASE mydb;
CREATE DATABASE mydb TEMPLATE my_template;

Cái này cực nhanh , bởi vì Postgres sao chép toàn bộ cấu trúc ở cấp độ tệp. Không có vấn đề tương tranh hoặc chi phí khác làm chậm bạn.

Nếu các kết nối đồng thời ngăn bạn bỏ DB, hãy xem xét:


1
Điều đáng chú ý là chức năng cuối cùng này đã xóa sạch TẤT CẢ cơ sở dữ liệu. Không chỉ là một người hiện đang kết nối .... vâng ... gọi tôi là ngây thơ nhưng điều đó thực sự không rõ ràng từ bài đăng này.
Amalgovinus

@Amalgovinus: Chức năng cuối cùng nào? Không có chức năng nào trong câu trả lời của tôi chạm vào bất cứ thứ gì ngoài cơ sở dữ liệu hiện tại (ngoại trừ DROP DATABASE mydb, rõ ràng). Bạn có nhầm lẫn các lược đồ với cơ sở dữ liệu, có thể?
Erwin Brandstetter

3
@Amalgovinus: Không, điều đó là không thể. Các DOlệnh (giống như bất kỳ câu lệnh SQL khác) được thực hiện trong cơ sở dữ liệu hiện hành độc quyền . Postgres không có cách nào để truy cập các cơ sở dữ liệu khác trong cùng một giao dịch. Bạn sẽ phải sử dụng dblink hoặc FDW để làm điều đó. Nhưng nó không ảnh hưởng đến tất cả các schema trong database hiện tại - trừ khi bạn thêm WHERE t.schemaname = 'public'để hạn chế ảnh hưởng đến một lược đồ cụ thể trong trường hợp đặc biệt này.
Erwin Brandstetter

1
Thực sự tốt đẹp để biết về những mẫu. Điều này có thể hữu ích cho tôi ngay cả trong các kịch bản kiểm tra tự động, trong đó có thể cần thiết lập lại / chuẩn bị cơ sở dữ liệu.
hbobenermo

3
Cảm ơn câu trả lời tuyệt vời, tôi đang sử dụng "Lệnh đơn, không vòng lặp" trả về lệnh TRUNCATE, tôi nên thực hiện nó như thế nào?
Mahyar

40

Nếu tôi phải làm điều này, tôi sẽ chỉ cần tạo một sql lược đồ của db hiện tại, sau đó thả và tạo db, sau đó tải db với lược đồ sql.

Dưới đây là các bước liên quan:

1) Tạo kết xuất Schema của cơ sở dữ liệu ( --schema-only)

pg_dump mydb -s > schema.sql

2) Thả cơ sở dữ liệu

drop database mydb;

3) Tạo cơ sở dữ liệu

create database mydb;

4) Lược đồ nhập khẩu

psql mydb < schema.sql


9

Trong trường hợp này, có lẽ sẽ tốt hơn nếu chỉ có một cơ sở dữ liệu trống mà bạn sử dụng làm mẫu và khi bạn cần làm mới, hãy bỏ cơ sở dữ liệu hiện có và tạo một cơ sở dữ liệu mới từ mẫu.



3

Bạn cũng có thể làm điều này với 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 "{}"

Bạn sẽ cần điều chỉnh tên lược đồ, mật khẩu và tên người dùng để phù hợp với lược đồ của bạn.


3

AUTO_INCREMENTPhiên bản làm sạch :

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

Các chàng trai cách tốt hơn và sạch sẽ là:

1) Tạo kết xuất Schema của cơ sở dữ liệu (chỉ --schema) pg_dump mydb -s> lược đồ

2) Thả cơ sở dữ liệu thả cơ sở dữ liệu mydb;

3) Tạo cơ sở dữ liệu tạo cơ sở dữ liệu mydb;

4) Nhập lược đồ psql mydb <lược đồ

Nó làm việc cho tôi!

Chúc một ngày tốt lành. Hiram Walker


2

Nếu bạn có thể sử dụng psql, bạn có thể sử dụng \gexeclệnh meta để thực hiện đầu ra truy vấn;

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 

Lưu ý rằng \gexecđược đưa vào phiên bản 9.6


1

Để xóa dữ liệu và bảo tồn cấu trúc bảng trong pgAdmin, bạn có thể thực hiện:

  • Nhấp chuột phải vào cơ sở dữ liệu -> sao lưu, chọn "Chỉ lược đồ"
  • Bỏ cơ sở dữ liệu
  • Tạo một cơ sở dữ liệu mới và đặt tên như trước đây
  • Nhấp chuột phải vào cơ sở dữ liệu mới -> khôi phục -> chọn bản sao lưu, chọn "Chỉ lược đồ"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.