PostgreSQL: List dependent rows - sql

How can I see which entries depend on a given entry using PostgreSQL ? (dependent meaning "having a foreign key referencing the entry").
Basically, I want to check which entries might be cascaded when I DELETE a given entry of a table.

To see all actual rows depending via fk constraint, identify the columns with the tools described below.
Where foreign key constraints are defined with ON DELETE CASCADE, depending rows will be deleted (possibly cascading the DELETE to more depending tables).
Where foreign key constraints are defined with ON DELETE SET NULL / ON DELETE SET DEFAULT, only the value in the columns will be reset to NULL / default value.
Else a DELETE on rows with dependent rows would fail with an exception.
Then run queries like the following on the identified tables / columns:
SELECT f.tbl_fk_id, f.col_fk
FROM tbl_fk f
JOIN tbl t ON f.col_fk = t.col
AND <same condition as DELETE here>;
pgAdmin supplies this feature:
Pick the the object in the object browser to the left and chose the dependents pane top right.
pgAdmin uses a couple of queries to the system catalog to assemble the list. You could log the commands issued if you want to build a query yourself.
Also, when deleting an object where you are not completely sure about dependents, try a plain DROP first (without CASCADE). You will get an error message if any dependent exists ...
And finally, but proceed with caution!, you can start a transaction and just issue the command:
BEGIN;
DROP TABLE tbl CASCADE;
Then, if you like what you see:
COMMIT;
If you don't:
ROLLBACK;
And it will be like it never happened. ;)
You will see something like this:
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to constraint tbl1_tbl_id_fkey on table myschema.tbl1
drop cascades to constraint tbl_winner_tbl_id_fkey on table myschema.tbl_foo
drop cascades to constraint bar_tbl_id_fkey on table myschema.bar
drop cascades to constraint tbl1_tbl_id_fkey on table x.tbl1
Query returned successfully with no result in 47 ms.

You can query this directly from the PostgreSQL system catalog:
SELECT
depending.relname as depending_table,
referenced.relname as referenced_table
FROM pg_catalog.pg_depend d
JOIN pg_catalog.pg_constraint fkey ON fkey.oid=d.objid AND fkey.contype='f'
JOIN pg_catalog.pg_class depending ON depending.oid=fkey.conrelid
JOIN pg_catalog.pg_class referenced ON referenced.oid=d.refobjid
WHERE fkey.confdeltype='c' -- just cascading deletes
AND referenced.oid != depending.oid -- ignoring reflexive dependencies
AND referenced.relkind='r' -- tables only
See this SQL Fiddle and the relevant documentation:
pg_depend for learning about dependencies
pg_constraint for learning about foreign keys
pg_class for learning about tables
Extending this to tell you the columns involved is left as an exercise for the reader. (Hint: pg_attribute.)

The existing answers use pg_catalog, which is OK but can incompatibly change between major versions of PostgreSQL. Wherever possible you should use the information_schema instead.
SELECT *
FROM information_schema.constraint_column_usage ccu
INNER JOIN information_schema.referential_constraints rc
USING (constraint_catalog, constraint_schema, constraint_name);
See:
referential_constraints
constraint_column_usage
constraint_table_usage

Related

Delete multiple rows of data from multiple tables in SQL

I need help of making a SQL query, when I delete the main category it should delete all the data of my subcategory and all the products related to that subcategory. Basically deleting data from 3 tables.
Can it be done in one query?
Cant give you code sample without a Database reference. But if all the tables have the same Primary key you can try using INNER JOIN.
DELETE table1, table2, table3
FROM table1
INNER JOIN table2 ON table2.key = table1.key
INNER JOIN table3 ON table3.key = table1.key
WHERE table1.key = value;
key value should be common accross all tables. Something like a "ID".
Instead of solving it via Sql, you could use foreign keys with the ON DELETE CASCADE setting enabled.
If you database type/version supports it.
F.e. ANSI SQL:2003, MySSQL, Oracle, PostgreSQL, MS SQL Server, SQLite
That way, when you delete from the main table. Then the records in the other table that reference to those deleted records, will automatically be deleted also.
For example in MS Sql Server:
ALTER TABLE SubCategory
ADD CONSTRAINT FK_SubCategory_MainCategoryID_Cascade
FOREIGN KEY (MainCategoryID)
REFERENCES MainCategory(ID) ON DELETE CASCADE;
ALTER TABLE Products
ADD CONSTRAINT FK_Products_SubCategoryID_Cascade
FOREIGN KEY (SubCategoryID)
REFERENCES SubCategory(ID) ON DELETE CASCADE;
It's a way to automatically maintain the referential integrity between them.
After that a delete from the MainCategory table will delete the related records from the SubCategory table. And also the Products that are related to the deleted SubCategory records.
And here is a db<>fiddle example for SQLite to demonstrate.
P.S. Personally I would prefere an ON DELETE SET NULL or an ON DELETE SET DEFAULT n, or even using triggers. Sure, that would require an extra cleanup of the unreferenced records afterwards, f.e. via a scheduled script. But it just feels less detrimental. Because then it's easier to fix when someone accidently deleted a MainCategory that shouldn't have been deleted.

Cannot drop table users because other objects depend on it

I want to drop my tables in my database.
But, when I use, for example,
DROP TABLE if exists users;
I receive this message:
cannot drop table users because other objects depend on it
I found the solution is to drop all tables. But, anyway, how to solve this problem without total data removal?
Use the cascade option:
DROP TABLE if exists users cascade;
this will drop any foreign key that is referencing the users table or any view using it.
It will not drop other tables (or delete rows from them).
If it was really necessary to drop that specific table with or without recreating it, then first find the object(s) that depends on it.
CREATE OR REPLACE VIEW admin.v_view_dependency AS
SELECT DISTINCT srcobj.oid AS src_oid
, srcnsp.nspname AS src_schemaname
, srcobj.relname AS src_objectname
, tgtobj.oid AS dependent_viewoid
, tgtnsp.nspname AS dependant_schemaname
, tgtobj.relname AS dependant_objectname
FROM pg_class srcobj
JOIN pg_depend srcdep ON srcobj.oid = srcdep.refobjid
JOIN pg_depend tgtdep ON srcdep.objid = tgtdep.objid
JOIN pg_class tgtobj ON tgtdep.refobjid = tgtobj.oid AND srcobj.oid <> tgtobj.oid
LEFT JOIN pg_namespace srcnsp ON srcobj.relnamespace = srcnsp.oid
LEFT JOIN pg_namespace tgtnsp ON tgtobj.relnamespace = tgtnsp.oid
WHERE tgtdep.deptype = 'i'::"char" AND tgtobj.relkind = 'v'::"char";
Then,
select top 99 * from admin.v_view_dependency where src_objectname like '%the_table_name_it_complaint_about%';
The result set will show you the dependant object in the field "dependant_objectname".
In general, to drop several interdependent tables you start from the tables that nothing depends on (the ones that have foreign keys pointing to other tables), and work backwards. E.g., if the table transactions depends on the table users, you'd drop transactions first. In short: Delete tables in the reverse order from how they were created.
If you manage to create tables with circular dependencies, you can first delete the foreign key constraint that prevents deletion. Or you can use the modifier CASCADE, which (as #a_horse explained in the comments), will drop any foreign key constraints that involve the deleted table. But note that not all DBMS's support CASCADE: Postgres does, but MySQL does not (the keyword is accepted but has no effect).

Update multiple table without knowing the table name (due to a chain of Foreign key relationship)

I need to update one field for a few rows in one table (say, Table_A). However, I'm getting an error message saying conflict with the Foreign Key Constraint in Table_B.
So, I tried to update Table_B as well, turns out Table_B has Foreign Key Constraint with Table_C and Table_D; again, I tried to update Table_C and D, turns out they are conflicting with table_E, F, G, H, I, J, K etc. etc. and on and on.
I was told that such "chain" can go up to 20+ tables.
Additionally, I do not have access to the database schema, thus it is extremely difficult for me to determine which field in which table is the foreign key for the other table.
Currently, all I can do is manually checking each table, all the way from A-Z by using select * statement from the table that is showing in the error message. I'm wondering if there is any alternative to update these specific fields all across tables A till (whichever the last table) directly?
I'm using SQL Server 2005.
This will give you the names of the tables and columns in your foreign keys
SELECT
OBJECT_NAME(fk.[constraint_object_id]) AS [foreign_key_name]
,OBJECT_SCHEMA_NAME(fk.[parent_object_id]) AS [parent_schema_name]
,OBJECT_NAME(fk.[parent_object_id]) AS [parent_table_name]
,pc.[name] AS [parent_column_name]
,OBJECT_SCHEMA_NAME(fk.[parent_object_id]) AS [referenced_schema_name]
,OBJECT_NAME(fk.[referenced_object_id]) AS [referenced_table_name]
,rc.[name] AS [referenced_column_name]
FROM [sys].[foreign_key_columns] fk
INNER JOIN [sys].[columns] pc ON
pc.[object_id] = fk.[parent_object_id] AND
pc.[column_id] = fk.[parent_column_id]
INNER JOIN [sys].[columns] rc ON
rc.[object_id] = fk.[referenced_object_id] AND
rc.[column_id] = fk.[referenced_column_id]
How to best display and analyze the connection graph is a more subjective matter and will depend on the complexity of your schema.

Delete rows from multiple tables in a database

I want to delete some records from a table based on criteria in another table. How do you delete from one of those tables without removing the records in both table?
I am looking to delete a table which are joined with other tables and the query looks something like this.
DELETE DeletingFromTable
FROM DeletingFromTable
INNER JOIN CriteriaTable ON DeletingFromTable.field_id = CriteriaTable.id
WHERE CriteriaTable.criteria = "value" ;
This should work:
DELETE DeleteFromTable FROM DeleteFromTable AS DT
JOIN CriteriaFromTable AS CT ON DT.SomeId = CT.SomeId
WHERE CT.SomeId=[value]
Your question is not 100% clear on what your issue is, but this query will drop tables 1,2 and 3 at the same time:
DROP TABLE table1,table2,table3
You can only delete data from one table at a time.
To delete from multiple table
Write multiple queries separated by semicolon and execute it at onces like
delete from table1;
delete from table2;
delete from table3;
Or you can write the procedure to do this task.
Please check this thread as well
Drop multiple tables in one shot in mysql
You can use:
DELETE FROM TableName
Which will remove all the data, but if you have any seeded columns, these will not be reset. If you want to DELETE data and reset the seeding of PK's, then use TRUNCATE...
TRUNCATE TABLE TableName
But, you need to consider whether you have other tables that have referential integrity, if this is the case, see this post here SQL Server: How to ignore referential integrity until COMMIT?
EDIT:
Your comment above...
delete query like this DELETE FROM table_name WHERE
some_column=some_value;
...suggests you are looking to delete specific rows?
You can just write a query to DROP the tables like so:
DROP TABLE [TABLE_1]
DROP TABLE [TABLE_2]
DROP TABLE [TABLE_3]
Depending on the tables and any constraints you may have between them, you will need to DROP the tables in the correct order.
If you right click any table (depending on SQL version), you should be able to 'View Dependencies'. If the 3 tables you are planning to DROP are only dependant on each other, you need to DROP the tables with no child dependencies first to avoid it failing.
For example, if you try to delete a parent table where it's primary key is referenced in a child table as a foreign key, the DROP will fail because of this. So deleting the child table with the foreign key first will allow you to subsequently DROP the parent table.
If however, the tables have other dependencies outside the tables you are deleting, you will need to remove the dependencies before this will work.

How to delete a lot of records from SQL database fast?

We have a table with about 1.5 million records. This table has a lot of FK relations to from different tables.
The problem is that 1 million record just duplicates which have to be deleted. We try to delete 1000 records at the time, but it's a very slow process.
What I have in mind is to copy temporarily records that have to stay to a new table.
Truncate existing one and copy records that have to stay back. With restoring primary key and all relations to the other tables. So from client side you cannot see any difference.
Not sure if it's an efficient way or not.
If it's I would love to see basic implementation of it so I can follow and apply to my case.
If not I would like to see efficient way of doing it.
Thank you
Our company has a bunch of temporary data stored in databases. When we need to delete a bunch of them, we break it up into a few hundred rows and delete them chunks at a time. We have an application whose sole purpose in life is to run a few queries like this over and over again:
with topFew as (select top 100 * from table) delete topFew
I suggest you whip up something simple like this, and just let it run for a few hours. Go work on something else while it's processing.
Performance of the delete can be improved by self joining the table using rowid. It can be even optimized by using a bulk collect and FORALL
DECLARE
limit_in integer;
CURSOR C1 is
Select min(b.rowid)
from table_name a, table_name b
where a.primary_key = b.primary_key;
TYPE C1_rec IS TABLE OF C1%ROWTYPE
INDEX BY PLS_INTEGER;
C1_record C1_rec
BEGIN
limit_in:=10000 --- Can be changed based on performance
OPEN C1;
LOOP
FETCH C1 BULK COLLECT INTO C1_record LIMIT limit_in;
FORALL indx in 1..c1_record.count
DELETE FROM table_name where row_id = C1_record(i);
commit;
END LOOP;
END;
The table that is to be deleted has child tables, So there will be a constraint Violation.
So before executing the above piece of code, It is a better option to alter the foreign key constraint TO HAVE DELETE CASCADE. We cannot modify a constraint to add delete cascade. So the foreign key should be dropped and recreated to have delete cascade
ALTER child_table
ADD CONSTRAINT fk_name
foreign_key (C1)
references parent_table (C2) on delete cascade;
Delete cascade would clean up your child tables as well..