Deleting from one table also deletes from other table - postgresql

Postgres on Linux
When I do the following command.
DELETE FROM some_table;
It also deletes data from another table. How do I found out how?

There is probably a foreign key constraint that points to your table and is defined with ON DELETE CASCADE.
Alternatively, there may be a trigger on the table that deletes the rows.
In psql, use \d some_table to see all such foreign keys and triggers.

Related

Replacing table and renaming primary key - Postgres

To preface, I am trying to replace an entire table with a new table with same columns, but with updated values.
I have the following SQL code:
BEGIN;
ALTER TABLE "original" RENAME TO "original_old";
ALTER TABLE "original_new" RENAME TO "original";
ALTER TABLE "original" RENAME CONSTRAINT "temp_original_id" to "original_id";
DROP TABLE "original_old";
COMMIT;
Output:
ERROR: constraint "temp_original_id" for table "original" does not exist
However, if I do the following before the last ALTER statement:
SELECT * from original;
I see temp_original_id present in the table.
I can't seem to find any other sources that lead me to updating primary key (at least that worked)
The table I am replacing also has dependencies with other tables.. So I was wondering if this would be a viable solution to even begin with
Did you mean ALTER TABLE "original" RENAME COLUMN "temp_original_id" to "original_id"; ?

How to safely reindex primary key on postgres?

We have a huge table that contains bloat on the primary key index. We constantly archive old records on that table.
We reindex other columns by recreating the index concurrently and dropping the old one. This is to avoid interfering with production traffic.
But this is not possible for a primary key since there are foreign keys depending on it. At least based on what we have tried.
What's the right way to reindex the primary key safely without blocking DML statements on the table?
REINDEX CONCURRENTLY seems to work as well. I tried it on my database and didn't get any error.
REINDEX INDEX CONCURRENTLY <indexname>;
I think it possibly does something similar to what #jlandercy has described in his answer. While the reindex was running I saw an index with suffix _ccnew and the existing one was intact as well. Eventually I guess that index was renamed as the original index after dropping the older one and I eventually see a unique primary index on my table.
I am using postgres v12.7.
You can use pg_repack for this.
pg_repack is a PostgreSQL extension which lets you remove bloat from tables and indexes, and optionally restore the physical order of clustered indexes.
It doesn't hold exclusive locks during the whole process. It still does execute some locks, but this should be for a short period of time only. You can check the details here: https://reorg.github.io/pg_repack/
To perform repack on indexes, you can try:
pg_repack -t table_name --only-indexes
TL;DR
Just reindex it as other index using its index name:
REINDEX INDEX <indexname>;
MCVE
Let's create a table with a Primary Key constraint which is also an Index:
CREATE TABLE test(
Id BIGSERIAL PRIMARY KEY
);
Looking at the catalogue we see the constraint name:
SELECT conname FROM pg_constraint WHERE conname LIKE 'test%';
-- "test_pkey"
Having the name of the index, we can reindex it:
REINDEX INDEX test_pkey;
You can also fix the Constraint Name at the creation:
CREATE TABLE test(
Id BIGSERIAL NOT NULL
);
ALTER TABLE test ADD CONSTRAINT myconstraint PRIMARY KEY(Id);
If you must address concurrence, then use the method a_horse_with_no_name suggested, create a unique index concurrently:
-- Ensure Uniqueness while recreating the Primary Key:
CREATE UNIQUE INDEX CONCURRENTLY tempindex ON test USING btree(Id);
-- Drop PK:
ALTER TABLE test DROP CONSTRAINT myconstraint;
-- Recreate PK:
ALTER TABLE test ADD CONSTRAINT myconstraint PRIMARY KEY(Id);
-- Drop redundant Index:
DROP INDEX tempindex;
To check Index existence:
SELECT * FROM pg_index WHERE indexrelid::regclass = 'tempindex'::regclass

Why are foreign keys active after disabling triggers on a table?

I'm trying to refresh some data that's referred to by other data - I want to truncate and reload the ms_automobile table, but the rm_automobile table has a foreign key to it.
It looks like the 'DISABLE TRIGGER' statements are working (run as postgres, a superuser):
mobilesurvey=# ALTER TABLE ms_automobile DISABLE TRIGGER ALL;
ALTER TABLE
mobilesurvey=# ALTER TABLE rm_automobile DISABLE TRIGGER ALL;
ALTER TABLE
But I can't then truncate the ms_automobile table:
mobilesurvey=# TRUNCATE TABLE ms_automobile;
ERROR: cannot truncate a table referenced in a foreign key constraint
DETAIL: Table "rm_automobile" references "ms_automobile".
HINT: Truncate table "rm_automobile" at the same time, or use TRUNCATE ... CASCADE.
Again, I do not want to lose the rm_automobile data; after the TRUNCATE I'm planning on doing a pg_restore that includes the missing ms_automobile data.
If possible, I'd like to disable instead of dropping the constraints - there are more of them, and maintaining disable/enable seems a lot less error-prone than maintaining drop/add.
So, how can I actually disable the foreign keys here?
Disabling triggers works as you expect on DELETE (and not on TRUNCATE).
DELETE FROM ms_automobile;
TRUNCATE is implemented in the specific way different from INSERT/UPDATE/DELETE. It doesn't use triggers but checks referential integrity once before its execution.

PostgreSQL constraints - ON DELETE CASCADE not being restored

I have run into problems when restoring a PostgreSQL database schema in another server. More precisely, some of the tables don't seem to have the same foreign key constraints associated with them that they used to in the original database. For example, the ON DELETE CASCADE clause seems to have completely evaporated from all of the constraint definitions.
That's probably because the dumping procedure didn't backup the ON DELETE CASCADE clauses in your table definitions.
Firstly you should delete the foreign key constraints on your tables and then go on to altering them:
Something like the following:
ALTER TABLE ONLY *your_table* DROP CONSTRAINT your_constraint;
After that, recreate the constraints with something like:
ALTER TABLE ONLY your_table ADD CONSTRAINT your_constraint (...ON DELETE CASCADE, etc..);

PostgreSQL - disabling constraints

I have a table with approx 5 million rows which has a fk constraint referencing the primary key of another table (also approx 5 million rows).
I need to delete about 75000 rows from both tables. I know that if I try doing this with the fk constraint enabled it's going to take an unacceptable amount of time.
Coming from an Oracle background my first thought was to disable the constraint, do the delete & then reenable the constraint. PostGres appears to let me disable constraint triggers if I am a super user (I'm not, but I am logging in as the user that owns/created the objects) but that doesn't seem to be quite what I want.
The other option is to drop the constraint and then reinstate it. I'm worried that rebuilding the constraint is going to take ages given the size of my tables.
Any thoughts?
edit: after Billy's encouragement I've tried doing the delete without changing any constraints and it takes in excess of 10 minutes. However, I have discovered that the table from which I'm trying to delete has a self referential foreign key ... duplicated (& non indexed).
Final update - I dropped the self referential foreign key, did my delete and added it back in. Billy's right all round but unfortunately I can't accept his comment as the answer!
Per previous comments, it should be a problem. That said, there is a command that may be what you're looking to - it'll set the constraints to deferred so they're checked on COMMIT, not on every delete. If you're doing just one big DELETE of all the rows, it won't make a difference, but if you're doing it in pieces, it will.
SET CONSTRAINTS ALL DEFERRED
is what you are looking for in that case. Note that constraints must be marked as DEFERRABLE before they can be deferred. For example:
ALTER TABLE table_name
ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2)
DEFERRABLE INITIALLY IMMEDIATE;
The constraint can then be deferred in a transaction or function as follows:
CREATE OR REPLACE FUNCTION f() RETURNS void AS
$BODY$
BEGIN
SET CONSTRAINTS ALL DEFERRED;
-- Code that temporarily violates the constraint...
-- UPDATE table_name ...
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
What worked for me was to disable one by one the TRIGGERS of those tables that are gonna be involved in the DELETE operation.
ALTER TABLE reference DISABLE TRIGGER ALL;
DELETE FROM reference WHERE refered_id > 1;
ALTER TABLE reference ENABLE TRIGGER ALL;
Solution is working in version 9.3.16. In my case time went from 45 minutes to 14 seconds executing DELETE operations.
As stated in the comments section by #amphetamachine, you will need to have admin privileges to the tables to perform this task.
If you try DISABLE TRIGGER ALL and get an error like permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (I got this on Amazon RDS), try this:
set session_replication_role to replica;
If this succeeds, all triggers that underlie table constraints will be disabled. Now it's up to you to make sure your changes leave the DB in a consistent state!
Then when you are done, reenable triggers & constraints for your session with:
set session_replication_role to default;
(This answer assumes your intent is to delete all of the rows of these tables, not just a selection.)
I also had to do this, but as part of a test suite. I found the answer, suggested elsewhere on SO. Use TRUNCATE TABLE as follows:
TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];
The following quickly deletes all rows from tables table1, table2, and table3, provided that there are no references to rows of these tables from tables not listed:
TRUNCATE TABLE table1, table2, table3;
As long as references are between the tables listed, PostgreSQL will delete all the rows without concern for referential integrity. If a table other than those listed references a row of one of these tables, the query will fail.
However, you can qualify the query so that it also truncates all tables with references to the listed tables (although I have not tried this):
TRUNCATE TABLE table1, table2, table3 CASCADE;
By default, the sequences of these tables do not restart numbering. New rows will continue with the next number of the sequence. To restart sequence numbering:
TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;
My PostgreSQL is 9.6.8.
set session_replication_role to replica;
work for me but I need permission.
I login psql with super user.
sudo -u postgres psql
Then connect to my database
\c myDB
And run:
set session_replication_role to replica;
Now I can delete from table with constraint.
Disable all table constraints
ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
-- Enable all table constraints
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName