PostgreSQL delete all content - postgresql

Hello I want to delete all data in my postgresql tables, but not the table itself.
How could I do this?

Use the TRUNCATE TABLE command.

The content of the table/tables in PostgreSQL database can be deleted in several ways.
Deleting table content using sql:
Deleting content of one table:
TRUNCATE table_name;
DELETE FROM table_name;
Deleting content of all named tables:
TRUNCATE table_a, table_b, …, table_z;
Deleting content of named tables and tables that reference to them (I will explain it in more details later in this answer):
TRUNCATE table_a, table_b CASCADE;
Deleting table content using pgAdmin:
Deleting content of one table:
Right click on the table -> Truncate
Deleting content of table and tables that reference to it:
Right click on the table -> Truncate Cascaded
Difference between delete and truncate:
From the documentation:
DELETE deletes rows that satisfy the WHERE clause from the specified
table. If the WHERE clause is absent, the effect is to delete all rows
in the table.
http://www.postgresql.org/docs/9.3/static/sql-delete.html
TRUNCATE is a PostgreSQL extension that provides a faster mechanism to
remove all rows from a table. TRUNCATE quickly removes all rows from a
set of tables. It has the same effect as an unqualified DELETE on each
table, but since it does not actually scan the tables it is faster.
Furthermore, it reclaims disk space immediately, rather than requiring
a subsequent VACUUM operation. This is most useful on large tables.
http://www.postgresql.org/docs/9.1/static/sql-truncate.html
Working with table that is referenced from other table:
When you have database that has more than one table the tables have probably relationship.
As an example there are three tables:
create table customers (
customer_id int not null,
name varchar(20),
surname varchar(30),
constraint pk_customer primary key (customer_id)
);
create table orders (
order_id int not null,
number int not null,
customer_id int not null,
constraint pk_order primary key (order_id),
constraint fk_customer foreign key (customer_id) references customers(customer_id)
);
create table loyalty_cards (
card_id int not null,
card_number varchar(10) not null,
customer_id int not null,
constraint pk_card primary key (card_id),
constraint fk_customer foreign key (customer_id) references customers(customer_id)
);
And some prepared data for these tables:
insert into customers values (1, 'John', 'Smith');
insert into orders values
(10, 1000, 1),
(11, 1009, 1),
(12, 1010, 1);
insert into loyalty_cards values (100, 'A123456789', 1);
Table orders references table customers and table loyalty_cards references table customers. When you try to TRUNCATE / DELETE FROM the table that is referenced by other table/s (the other table/s has foreign key constraint to the named table) you get an error. To delete content from all three tables you have to name all these tables (the order is not important)
TRUNCATE customers, loyalty_cards, orders;
or just the table that is referenced with CASCADE key word (you can name more tables than just one)
TRUNCATE customers CASCADE;
The same applies for pgAdmin. Right click on customers table and choose Truncate Cascaded.

For small tables DELETE is often faster and needs less aggressive locking (important for concurrent load):
DELETE FROM tbl;
With no WHERE condition.
For medium or bigger tables, go with TRUNCATE, like #Greg posted:
TRUNCATE tbl;
Hard to pin down the line between "small" and "big", as that depends on many variables. You'll have to test in your installation.

I found a very easy and fast way for everyone who might use a tool like DBeaver:
You just need to select all the tables that you want to truncate (SHIFT + click or CTRL + click) then right click
And if you have foreign keys, select also CASCADE option on Settings panel. Start and that's all it takes!

Related

Replacing two columns (first name, last name) with an auto-increment id

I have a time-series location data table containing the following columns (time, first_name, last_name, loc_lat, loc_long) with the first three columns as the primary key. The table has more than 1M rows.
I notice that first_name and last_name duplicate quite often. There are only 100 combinations in 1M rows. Therefore, to save disk space, I am thinking about creating a separate people table with columns (id, first_name, last_name) where (first_name, last_name) is a unique constraint, in order to simplify the time-series location table to be (time, person_id, loc_lat, loc_long) where person_id is a foreign key for the people table.
I want to first create a new table from my existing 1M row table to test if there is indeed meaningful disk space save with this change. I feel like this task is quite doable but cannot find a concrete way to do so yet. Any suggestions?
That's a basic step of database normalization.
If you can afford to do so, it will be faster to write a new table exchanging full names for IDs, than altering the schema of the existing table and update all rows. Basically:
BEGIN; -- wrap in single transaction (optional, but safer)
CREATE TABLE people (
people_id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, first_name text NOT NULL
, last_name text NOT NULL
, CONSTRAINT full_name_uni UNIQUE (first_name, last_name)
);
INSERT INTO people (first_name, last_name)
SELECT DISTINCT first_name, last_name
FROM tbl
ORDER BY 1, 2; -- optional
ALTER TABLE tbl RENAME TO tbl_old; -- free up org. table name
CREATE TABLE tbl AS
SELECT t.time, p.people_id, t.loc_lat, t.loc_long
FROM tbl_old t
JOIN people p USING (first_name, last_name);
-- ORDER BY ??
ALTER TABLE tbl ADD CONSTRAINT people_id_fk FOREIGN KEY (people_id) REFERENCES people(people_id);
-- make sure the new table is complete. indexes? constraints?
-- Finally:
DROP TABLE tbl_old;
COMMIT;
Related:
Best way to populate a new column in a large table?
Add new column without table lock?
Updating database rows without locking the table in PostgreSQL 9.2
DISTINCT is simple. But for only 100 distinct full names - and with the right index support! - there are more sophisticated, (much) faster ways. See:
Optimize GROUP BY query to retrieve latest row per user

Avoid scan on attach partition with check constraint

I am recreating an existing table as a partitioned table in PostgreSQL 11.
After some research, I am approaching it using the following procedure so this can be done online while writes are still happening on the table:
add a check constraint on the existing table, first as not valid and then validating
drop the existing primary key
rename the existing table
create the partitioned table under the prior table name
attach the existing table as a partition to the new partitioned table
My expectation was that the last step would be relatively fast, but I don't really have a number for this. In my testing, it's taking about 30s. I wonder if my expectations are incorrect or if I'm doing something wrong with the constraint or anything else.
Here's a simplified version of the DDL.
First, the inserted_at column is declared like this:
inserted_at timestamp without time zone not null
I want to have an index on the ID even after I drop the PK for existing queries and writes, so I create an index:
create unique index concurrently my_events_temp_id_index on my_events (id);
The check constraint is created in one transaction:
alter table my_events add constraint my_events_2022_07_events_check
check (inserted_at >= '2018-01-01' and inserted_at < '2022-08-01')
not valid;
In the next transaction, it's validated (and the validation is successful):
alter table my_events validate constraint my_events_2022_07_events_check;
Then before creating the partitioned table, I drop the primary key of the existing table:
alter table my_events drop constraint my_events_pkey cascade;
Finally, in its own transaction, the partitioned table is created:
alter table my_events rename to my_events_2022_07;
create table my_events (
id uuid not null,
... other columns,
inserted_at timestamp without time zone not null,
primary key (id, inserted_at)
) partition by range (inserted_at);
alter table my_events attach partition my_events_2022_07
for values from ('2018-01-01') to ('2022-08-01');
That last transaction blocks inserts and takes about 30s for the 12M rows in my test database.
Edit
I wanted to add that in response to the attach I see this:
INFO: partition constraint for table "my_events_2022_07" is implied by existing constraints
That makes me think I'm doing this right.
The problem is not the check constraint, it is the primary key.
If you make the original unique index include both columns:
create unique index concurrently my_events_temp_id_index on my_events (id,inserted_at);
And if you make the new table have a unique index rather than a primary key on those two columns, then the attach is nearly instantaneous.
These seem to me like unneeded restrictions in PostgreSQL, both that the unique index on one column can't be used to imply uniqueness on the both columns, and that the unique index on both columns cannot be used to imply the primary key (nor even a unique constraint--but only a unique index).

PostgreSQL create partitions of table with existing rows and with referential integrity

I'm trying to partitioning a table with existing rows in Postgresql 10.8.
The structure is like this :
I'm trying to create partitions of table Item, it has around 5mill of rows.
I create the partitions with those commands:
CREATE TABLE item_1 (CHECK (id >0 AND id <1000001)) INHERITS (item);
CREATE TABLE item_2 (CHECK (id >1000000 AND id <2000001)) INHERITS (item);
...
Then the rules:
CREATE RULE item_1_rule AS ON INSERT TO item WHERE (id >0 AND id <1000001) DO INSTEAD INSERT INTO item_1 VALUES (NEW.*);
CREATE RULE item_2_rule AS ON INSERT TO item WHERE (id >1000000 AND id <2000001) DO INSTEAD INSERT INTO item_2 VALUES (NEW.*);
...
Then the migration to the partitioned tables:
INSERT INTO item_1 SELECT * FROM item WHERE (id >0 AND id <1000001);
INSERT INTO item_2 SELECT * FROM item WHERE (id >1000000 AND id <2000001);
...
And finally I try to Delete all the rows from the Item table:
DELETE FROM ONLY item;
But I get this error:
ERROR: update or delete on table "item" violates foreign key constraint >"item_audit_item_id_fkey" on table "item_audit"
SQL state: 23503
Detail: Key (id)=(1) is still referenced from table "item_audit".
So is it possible to drop the data from the main table Item to have only the rows on the partitions tables?
There are other alternatives to make a partition in a different way?
If you use inheritance partitioning, you won't be able to have foreign keys pointing to the partitioned table. You have to drop the constraint before you can delete the rows.
I recommend to use declarative partitioning. If you upgrade to v11 or later, you can have a foreign key pointing to a partitioned table, but since the primary key on a partitioned table has to contain the partitioning key, you'd have to add that column to the foreign key as well.
Since partitioning is mostly useful for mass data deletion, it might make sense to partition item_audit the same way.

Does a postgres foreign key imply an index?

I have a postgres table (lets call this table Events) with a composite foreign key to another table (lets call this table Logs). The Events table looks like this:
CREATE TABLE Events (
ColPrimary UUID,
ColA VARCHAR(50),
ColB VARCHAR(50),
ColC VARCHAR(50),
PRIMARY KEY (ColPrimary),
FOREIGN KEY (ColA, ColB, ColC) REFERENCES Logs(ColA, ColB, ColC)
);
In this case, I know that I can efficiently search for Events by the primary key, and join to Logs.
What I am interested in is if this foreign key creates an index on the Events table which can be useful even without joining. For example, would the following query benefit from the FK?
SELECT * FROM Events
WHERE ColA='foo' AND ColB='bar'
Note: I have run the POSTGRES EXPLAIN for a very similar case to this, and see that the query will result in a full table scan. I am not sure if this is because the FK is not helpful for this query, or if my data size is small and a scan is more efficient at my current scale.
PostgreSQL does not automatically create an index on the columns on which a foreign key is defined. If you need such an index, you will have to create it yourself.
It is usually a good idea to have such an index, so that modifications on the parent table that affect the referenced columns are efficient.

Keep sequence created from BIGSERIAL when deleting table

I have a postgres table creating with the following SQL:
CREATE TABLE mytable (
mytable_id BIGSERIAL NOT NULL,
mytable_char VARCHAR(8) NOT NULL
)
This creates the table as well as an implicit mytable_mytable_id_seq sequence.
Now, after creating 1.000.000 records, I want to split this table into partitioned tables (using inheritance). Because I link refer to the main table from other tables, I want to keep using the IDs from the original table in the new child tables and keep using the sequence.
However, if I do DROP TABLE mytable it also deletes the sequence. How can I keep the sequence when dropping the table?
You need to first remove the association between the column and the sequence:
alter sequence mytable_mytable_id_seq owned by none;
If you now drop the table, the sequence will not be dropped.
Details are in the manual: http://www.postgresql.org/docs/current/static/sql-altersequence.html
An alternative is to create a new sequence and set that to the value of the existing sequence:
create sequence part_seq;
select setval('part_seq', (select nextval('mytable_mytable_id_seq'), false);