I regularly need to delete all the data from my PostgreSQL database before a rebuild. How would I do this directly in SQL?
At the moment I've managed to come up with a SQL statement that returns all the commands I need to execute:
SELECT 'TRUNCATE TABLE ' || tablename || ';' FROM pg_tables WHERE tableowner='MYUSER';
But I can't see a way to execute them programmatically once I have them.
FrustratedWithFormsDesigner is correct, PL/pgSQL can do this. Here's the script:
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;
This creates a stored function (you need to do this just once) which you can afterwards use like this:
SELECT truncate_tables('MYUSER');
Explicit cursors are rarely needed in plpgsql. Use the simpler and faster implicit cursor of a FOR
loop:
Note: Since table names are not unique per database, you have to schema-qualify table names to be sure. Also, I limit the function to the default schema 'public'. Adapt to your needs, but be sure to exclude the system schemas pg_*
and information_schema
.
Be very careful with these functions. They nuke your database. I added a child safety device. Comment the RAISE NOTICE
line and uncomment EXECUTE
to prime the bomb ...
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
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
format('TRUNCATE TABLE %I.%I CASCADE', _sch, _tbl);
END LOOP;
END
$func$ LANGUAGE plpgsql;
format()
requires Postgres 9.1 or later. In older versions concatenate the query string like this:
'TRUNCATE TABLE ' || quote_ident(_sch) || '.' || quote_ident(_tbl) || ' CASCADE';
Since we can TRUNCATE
multiple tables at once we don't need any cursor or loop at all:
Aggregate all table names and execute a single statement. Simpler, faster:
CREATE OR REPLACE FUNCTION f_truncate_tables(_username text)
RETURNS void AS
$func$
BEGIN
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
(SELECT 'TRUNCATE TABLE '
|| string_agg(format('%I.%I', schemaname, tablename), ', ')
|| ' CASCADE'
FROM pg_tables
WHERE tableowner = _username
AND schemaname = 'public'
);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT truncate_tables('postgres');
You don't even need a function. In Postgres 9.0+ you can execute dynamic commands in a DO
statement. And in Postgres 9.5+ the syntax can be even simpler:
DO
$func$
BEGIN
-- dangerous, test before you execute!
RAISE NOTICE '%', -- once confident, comment this line ...
-- EXECUTE -- ... and uncomment this one
(SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' CASCADE'
FROM pg_class
WHERE relkind = 'r' -- only tables
AND relnamespace = 'public'::regnamespace
);
END
$func$;
About the difference between pg_class
, pg_tables
and information_schema.tables
:
About regclass
and quoted table names:
Create a "template" database (let's name it my_template
) with your vanilla structure and all empty tables. Then go through a DROP
/CREATE DATABASE
cycle:
DROP DATABASE mydb;
CREATE DATABASE mydb ;
This is extremely fast , because Postgres copies the whole structure on the file level. No concurrency issues or other overhead slowing you down.
If concurrent connections keep you from dropping the DB, consider:
If I have to do this, I will simply create a schema sql of current db, then drop & create db, then load db with schema sql.
Below are the steps involved:
1) Create Schema dump of database ( --schema-only
)
pg_dump mydb -s > schema.sql
2) Drop database
drop database mydb;
3) Create Database
create database mydb;
4) Import Schema
psql mydb < schema.sql
在这种情况下,最好只使用一个空数据库作为模板,当您需要刷新时,删除现有数据库并从模板创建一个新数据库。
Just execute the query bellow:
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || '';
END LOOP;
END $$;
Guys the better and clean way is to :
1) Create Schema dump of database (--schema-only) pg_dump mydb -s > schema.sql
2) Drop database drop database mydb;
3) Create Database create database mydb;
4) Import Schema psql mydb < schema.sql
It´s work for me!
Have a nice day. Hiram Walker
Could you use dynamic SQL to execute each statement in turn? You would probably have to write a PL/pgSQL script to do this.
http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html (section 38.5.4. Executing Dynamic Commands)
Cleaning AUTO_INCREMENT
version:
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;
You can do this with bash also:
#!/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 "{}"
You will need to adjust schema names, passwords and usernames to match your schemas.
If you can use psql you can use \\gexec
meta command to execute query output;
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
Note that \\gexec
is introduced into the version 9.6
Simply, you can run this piece of SQL:
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname =current_schema()) LOOP
EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
For removing the data and preserving the table-structures in pgAdmin you can do:
You can use something like this to get all truncate queries.
SELECT 'TRUNCATE TABLE ' || table_name || ';'
FROM information_schema.tables
WHERE table_schema='schema_name'
AND table_type='BASE TABLE';
One thing that I don't see here is truncating and then resetting sequences. Note that a simple truncate like all that have been given here will just truncate the tables, but will leave sequences at their pre-truncate values. To reset the sequences to their start values when you truncate do:
TRUNCATE TABLE table_name RESTART IDENTITY CASCADE;
you can just add that RESTART IDENTITY to any of the answers you fancy, no need to repeat that here. CASCADE is there for any foreign key constraints you may face.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.