Postgres: ล้างฐานข้อมูลทั้งหมดก่อนที่จะสร้าง / เติมข้อมูลอีกครั้งจากสคริปต์ทุบตี


139

ฉันกำลังเขียนเชลล์สคริปต์ (จะกลายเป็น cronjob) ที่จะ:

1: ถ่ายโอนฐานข้อมูลการผลิตของฉัน

2: นำเข้าดัมพ์ลงในฐานข้อมูลการพัฒนาของฉัน

ระหว่างขั้นตอนที่ 1 ถึง 2 ฉันต้องล้างฐานข้อมูลการพัฒนา (ปล่อยตารางทั้งหมดหรือไม่) วิธีนี้ทำได้ดีที่สุดจากเชลล์สคริปต์หรือไม่ จนถึงตอนนี้ดูเหมือนว่านี้:

#!/bin/bash
time=`date '+%Y'-'%m'-'%d'`
# 1. export(dump) the current production database
pg_dump -U production_db_name > /backup/dir/backup-${time}.sql

# missing step: drop all tables from development database so it can be re-populated

# 2. load the backup into the development database
psql -U development_db_name < backup/dir/backup-${time}.sql

3
oneliner สำหรับผู้ที่รีบร้อน:dbname='db_name' && dropdb $dbname && createdb $dbname && psql -d $dbname -f dump.sql
ruuter

oneliner นี้ต้องการให้คุณมีสิทธิ์ในการสร้าง / วางฐานข้อมูล วิธีการที่ผู้เขียนพยายามไม่ต้องการสิทธิ์พิเศษ
ribamar

คำตอบ:


188

ฉันจะทิ้งฐานข้อมูลแล้วสร้างใหม่อีกครั้ง บนระบบ UNIX หรือ Linux ที่ควรทำ:

$ dropdb development_db_name
$ createdb developmnent_db_name

นั่นเป็นวิธีที่ฉันทำ


นี่คือวิธีที่ฉันทำเช่นกัน จากนั้นเพียงคืนค่าเป็น db ที่สร้างขึ้นใหม่
Arthur Thomas

3
อ๋อ สิ่งนี้ดีกว่าเนื่องจากอาจมีวัตถุที่ไม่ใช่ส่วนหนึ่งของดัมพ์ที่คุณกำลังกู้คืน ในกรณีนี้พวกเขาจะถูกฆ่าตายอย่างแน่นอน
pstanton

7
เคล็ดลับหนึ่งที่ช่วยประหยัดเวลาของฉันคือ $ sudo -u postgres dropdb DATABASE_NAME
Alvin

36
แต่ ... การอนุญาตและการเป็นเจ้าของฐานข้อมูลล่ะ?
Emanuele Paolini

6
@EmanuelePaolini createdb --owner=db_owner [--template=template0 --encoding=UTF8] db_nameฉันจะเพิ่มสองคนสุดท้ายโดยค่าเริ่มต้นฐานข้อมูลทั้งหมด
mcalex

91

หากคุณไม่ต้องการสำรองข้อมูลของฐานข้อมูลที่ถูกทิ้งลงดิสก์ในรูปแบบไฟล์สคริปต์. sql แบบข้อความล้วนคุณสามารถเชื่อมต่อpg_dumpและpg_restoreรวมเข้าด้วยกันโดยตรงผ่านไพพ์

หากต้องการวางและสร้างตารางใหม่คุณสามารถใช้--cleanตัวเลือกบรรทัดคำสั่งpg_dumpเพื่อปล่อยคำสั่ง SQL เพื่อล้างวัตถุฐานข้อมูล (วาง) ก่อนที่จะสร้างคำสั่งนั้น (สิ่งนี้จะไม่ลบฐานข้อมูลทั้งหมดเพียงแค่แต่ละตาราง / ลำดับ / ดัชนี / ฯลฯ ก่อนสร้างใหม่)

ทั้งสองข้างต้นจะมีลักษณะดังนี้:

pg_dump -U username --clean | pg_restore -U username

1
ฉันชอบโซลูชันนี้เนื่องจากฉันต้องการสำเนาสำรองตอนนี้ฉันกำลังทำสิ่งนี้: pg_dump -Ft -U production_db_name> /backup/dir/backup-${time}.tar pg_restore -U development_db_name -d development_db_name -O - -clean /backup/dir/backup-${time}.tar ทำงานเหมือนมีเสน่ห์ขอบคุณสำหรับความช่วยเหลือของคุณ!
ฮอฟ

38
ระวัง: ตัวเลือก --clean จะลบความสัมพันธ์เหล่านั้นที่พบในไฟล์กู้คืนเท่านั้น ซึ่งหมายความว่าหากคุณเพิ่มตารางสำหรับการทดสอบแล้วต้องการลบออก (เพื่อซิงโครไนซ์กับฐานข้อมูลการผลิตเป็นต้น) มันจะไม่ถูกลบ
ianaré

6
สิ่งสำคัญคือต้องทราบว่าตัวเลือก--cleanของpg_dumpทำงานได้กับการสำรองข้อมูลข้อความธรรมดาเท่านั้น เนื่องจากเอกสารระบุอย่างชัดเจนที่นี่postgresql.org/docs/9.4/static/app-pgdump.htmlคุณต้องใช้ --clean บน pg_restore เพื่อการสำรองข้อมูลที่เก็บถาวร
Kikin-Sama

6
มีวิธีการรวมน้ำตกในตัวเลือก "- สะอาด" ใด ๆ มันเป็นตัวเลือกที่ดูไร้ประโยชน์ ฉันได้รับ "ข้อผิดพลาด: ไม่สามารถวางสคีมาสู่สาธารณะได้เนื่องจากออบเจ็กต์อื่นขึ้นอยู่กับมัน" เช่นใช้เวลา 100%
user4674453

คำถามที่ถามเกี่ยวกับการลบตารางทั้งหมด สิ่งนี้จะลบตารางที่พบในฐานข้อมูลที่ pg_dump ทิ้งเท่านั้น
jbg

13

แม้ว่าบรรทัดต่อไปนี้มาจากสคริปต์แบตช์ windows คำสั่งควรคล้ายกันมาก:

psql -U username -h localhost -d postgres -c "DROP DATABASE \"$DATABASE\";"

คำสั่งนี้ใช้เพื่อล้างฐานข้อมูลทั้งหมดโดยการวางมันจริง $DATABASE(ใน Windows ควรจะเป็น%DATABASE%) ในคำสั่งเป็นตัวแปรสภาพแวดล้อมหน้าต่างสไตล์ที่ประเมินชื่อฐานข้อมูล development_db_nameคุณจะต้องทดแทนว่าโดยคุณ


4
ถ้าเช่นนั้นทำไมไม่ใช้คำสั่งdropdbและcreatedbคำสั่งที่มีอยู่แล้ว? หากคุณสามารถเรียกใช้ psql คุณสามารถเรียกใช้ psql ได้เช่นกัน
Kamermans ของไมค์ 'Pomax'

10

วิธีถ่ายโอนข้อมูล:

pg_dump -Fc mydb > db.dump

ในการกู้คืน:

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d my_db db/latest.dump

7

หากคุณต้องการล้างฐานข้อมูลชื่อ "example_db":

1) ลงชื่อเข้าใช้ฐานข้อมูลอื่น (ตัวอย่างเช่น 'postgres'):

psql postgres

2) ลบฐานข้อมูลของคุณ:

DROP DATABASE example_db;

3) สร้างฐานข้อมูลของคุณใหม่:

CREATE DATABASE example_db;


4

หมายเหตุ: คำตอบของฉันเกี่ยวกับการลบตารางและวัตถุฐานข้อมูลอื่น ๆ สำหรับการลบข้อมูลทั้งหมดในตารางเช่นการตัดตารางทั้งหมด Endre ทั้งสองได้จัดทำคำสั่งที่ดำเนินการอย่างดี (การดำเนินการโดยตรง) ในทำนองเดียวกันในเดือนต่อมา

สำหรับกรณีที่คุณไม่สามารถเพียงแค่DROP SCHEMA public CASCADE;, DROP OWNED BY current_user;หรือบางสิ่งบางอย่างที่นี่เป็นสคริปต์ SQL แบบสแตนด์อะโลนที่ผมเขียนซึ่งเป็นธุรกรรมที่ปลอดภัย (เช่นคุณสามารถวางไว้ระหว่างBEGIN;และทั้งROLLBACK;ที่จะเพียงแค่การทดสอบออกหรือCOMMIT;ทำจริงโฉนด) และ ล้างค่าวัตถุฐานข้อมูล“ ทั้งหมด” …ทั้งหมดที่ใช้ในฐานข้อมูลที่แอปพลิเคชันของเราใช้หรือฉันอาจเพิ่มอย่างสมเหตุสมผลซึ่งก็คือ:

  • ทริกเกอร์บนตาราง
  • ข้อ จำกัด ในตาราง (FK, PK CHECK,, UNIQUE)
  • ดัชนี
  • VIEWs (ปกติหรือปรากฏขึ้น)
  • ตาราง
  • ลำดับ
  • รูทีน (ฟังก์ชั่นรวม, ฟังก์ชั่น, ขั้นตอน)
  • publicschemata nōn-default (เช่นไม่ใช่หรือ DB-internal) schemata“ เรา” เป็นของตัวเอง: สคริปต์มีประโยชน์เมื่อรันเป็น“ ไม่ใช่ superuser ฐานข้อมูล”; superuser สามารถปล่อยschemata ทั้งหมด (อันที่สำคัญจริงๆยังคงยกเว้นอย่างชัดเจน)
  • ส่วนขยาย (สนับสนุนโดยผู้ใช้ แต่โดยปกติฉันจงใจปล่อยไว้)

ไม่ลดลงเป็น (บางเจตนาบางเพียงเพราะฉันไม่มีตัวอย่างในฐานข้อมูลของเรา):

  • publicสคี (เช่นสำหรับสิ่งที่ส่วนขยายที่ให้ไว้ในพวกเขา)
  • การเรียงหน้าและสิ่งอื่น ๆ ในโลแคล
  • ทริกเกอร์เหตุการณ์
  • เนื้อหาการค้นหาข้อความ ... (ดูที่นี่สำหรับสิ่งอื่นที่ฉันอาจพลาด)
  • บทบาทหรือการตั้งค่าความปลอดภัยอื่น ๆ
  • ประเภทคอมโพสิต
  • ตารางขนมปังปิ้ง
  • FDW และตารางต่างประเทศ

นี่คือจริงๆมีประโยชน์สำหรับกรณีที่การถ่ายโอนข้อมูลที่คุณต้องการที่จะเรียกคืนเป็นรุ่นคีมาฐานข้อมูลที่แตกต่างกัน (เช่นกับ Debian dbconfig-common, เสียหายหรือ Liquibase / DB-Manul) มากกว่าฐานข้อมูลที่คุณต้องการเรียกคืนมันเข้าไป

ฉันยังมีรุ่นที่ลบ“ ทุกอย่างยกเว้นสองตารางและสิ่งที่เป็นของพวกเขา” (ลำดับทดสอบด้วยตนเองขอโทษฉันรู้น่าเบื่อ) ในกรณีที่ใครบางคนสนใจ ความต่างมีขนาดเล็ก ติดต่อฉันหรือตรวจสอบ repo นี้ถ้าสนใจ

SQL

-- Copyright © 2019, 2020
--      mirabilos <t.glaser@tarent.de>
--
-- Provided that these terms and disclaimer and all copyright notices
-- are retained or reproduced in an accompanying document, permission
-- is granted to deal in this work without restriction, including un‐
-- limited rights to use, publicly perform, distribute, sell, modify,
-- merge, give away, or sublicence.
--
-- This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
-- the utmost extent permitted by applicable law, neither express nor
-- implied; without malicious intent or gross negligence. In no event
-- may a licensor, author or contributor be held liable for indirect,
-- direct, other damage, loss, or other issues arising in any way out
-- of dealing in the work, even if advised of the possibility of such
-- damage or existence of a defect, except proven that it results out
-- of said person’s immediate fault when using the work as intended.
-- -
-- Drop everything from the PostgreSQL database.

DO $$
DECLARE
        q TEXT;
        r RECORD;
BEGIN
        -- triggers
        FOR r IN (SELECT pns.nspname, pc.relname, pt.tgname
                FROM pg_catalog.pg_trigger pt, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pt.tgrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pt.tgisinternal=false
            ) LOOP
                EXECUTE format('DROP TRIGGER %I ON %I.%I;',
                    r.tgname, r.nspname, r.relname);
        END LOOP;
        -- constraints #1: foreign key
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype='f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- constraints #2: the rest
        FOR r IN (SELECT pns.nspname, pc.relname, pcon.conname
                FROM pg_catalog.pg_constraint pcon, pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace AND pc.oid=pcon.conrelid
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pcon.contype<>'f'
            ) LOOP
                EXECUTE format('ALTER TABLE ONLY %I.%I DROP CONSTRAINT %I;',
                    r.nspname, r.relname, r.conname);
        END LOOP;
        -- indicēs
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='i'
            ) LOOP
                EXECUTE format('DROP INDEX %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- normal and materialised views
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind IN ('v', 'm')
            ) LOOP
                EXECUTE format('DROP VIEW %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- tables
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='r'
            ) LOOP
                EXECUTE format('DROP TABLE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- sequences
        FOR r IN (SELECT pns.nspname, pc.relname
                FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pns
                WHERE pns.oid=pc.relnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pc.relkind='S'
            ) LOOP
                EXECUTE format('DROP SEQUENCE %I.%I;',
                    r.nspname, r.relname);
        END LOOP;
        -- extensions (only if necessary; keep them normally)
        FOR r IN (SELECT pns.nspname, pe.extname
                FROM pg_catalog.pg_extension pe, pg_catalog.pg_namespace pns
                WHERE pns.oid=pe.extnamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
            ) LOOP
                EXECUTE format('DROP EXTENSION %I;', r.extname);
        END LOOP;
        -- aggregate functions first (because they depend on other functions)
        FOR r IN (SELECT pns.nspname, pp.proname, pp.oid
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns, pg_catalog.pg_aggregate pagg
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
                    AND pagg.aggfnoid=pp.oid
            ) LOOP
                EXECUTE format('DROP AGGREGATE %I.%I(%s);',
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- routines (functions, aggregate functions, procedures, window functions)
        IF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='prokind' -- PostgreSQL 11+
            ) THEN
                q := 'CASE pp.prokind
                        WHEN ''p'' THEN ''PROCEDURE''
                        WHEN ''a'' THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSIF EXISTS (SELECT * FROM pg_catalog.pg_attribute
                WHERE attrelid='pg_catalog.pg_proc'::regclass
                    AND attname='proisagg' -- PostgreSQL ≤10
            ) THEN
                q := 'CASE pp.proisagg
                        WHEN true THEN ''AGGREGATE''
                        ELSE ''FUNCTION''
                    END';
        ELSE
                q := '''FUNCTION''';
        END IF;
        FOR r IN EXECUTE 'SELECT pns.nspname, pp.proname, pp.oid, ' || q || ' AS pt
                FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pns
                WHERE pns.oid=pp.pronamespace
                    AND pns.nspname NOT IN (''information_schema'', ''pg_catalog'', ''pg_toast'')
            ' LOOP
                EXECUTE format('DROP %s %I.%I(%s);', r.pt,
                    r.nspname, r.proname,
                    pg_get_function_identity_arguments(r.oid));
        END LOOP;
        -- nōn-default schemata we own; assume to be run by a not-superuser
        FOR r IN (SELECT pns.nspname
                FROM pg_catalog.pg_namespace pns, pg_catalog.pg_roles pr
                WHERE pr.oid=pns.nspowner
                    AND pns.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'public')
                    AND pr.rolname=current_user
            ) LOOP
                EXECUTE format('DROP SCHEMA %I;', r.nspname);
        END LOOP;
        -- voilà
        RAISE NOTICE 'Database cleared!';
END; $$;

ทดสอบยกเว้นการเพิ่มเติมในภายหลัง ( extensionsสนับสนุนโดยClémentPrévost ) บน PostgreSQL 9.6 ( jessie-backports) การทดสอบการกำจัดแบบรวมในวันที่ 9.6 และ 12.2 การทดสอบขั้นตอนการทดสอบบน 12.2 เช่นกัน ยินดีต้อนรับการแก้ไขข้อผิดพลาดและการปรับปรุงเพิ่มเติม!

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