Cannot drop table users because other objects depend on it - sql

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).

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.

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.

PostgreSQL: List dependent rows

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

Avoid Database Cursor in SQL Server

I have a bit of a puzzle (at least for me) which I am hoping is mostly because I am not yet an SQL master of the universe. Basically I have three tables:
Table A, Table B, and Table C.
Table C has a FK (Foriegn Key) to Table B, which has FK to Table A. (Each of these is many to one)
I need to remove an entry from Table A and of course all of it's corresponding entries from Tables B and C. In the past I've used a cursor to do this, selecting all the entries in Table B and cycling through each one to delete all their corresponding entries in Table C. Now this works - and has been working fine, but I suspect/hope there is a better way to achieve this effect without the use of cursors. So that's my question - how can I do this without using a cursor, or can it be done?
(Please let me know if I haven't been clear - I'll try to fix up the question).
Declare your FOREIGN KEYs as ON DELETE CASCADE
You could do this a couple ways...
You could just use cascading deletes on your foreign keys.
CREATE TABLE TableB
(FKColumn INT,
CONSTRAINT MyFk FOREIGN KEY (FKColumn)
REFERENCES TableA(PKColumn) ON DELETE CASCADE)
You could use delete triggers on each table to delete the related records.
CREATE TRIGGER cascade_triggerA
ON TableA
FOR DELETE
AS
BEGIN
DELETE TableB
FROM TableB JOIN DELETED ON TableB.FKColumn = DELETED.PKColumn
END
CREATE TRIGGER cascade_triggerB
ON TableB
FOR DELETE
AS
BEGIN
DELETE TableC
FROM TableC JOIN DELETED ON TableC.FKColumn = DELETED.PKColumn
END
If you're using MS SQL server, you could also use INSTEAD OF DELETE triggers. In this case, you'd create the trigger just on TableA - and in the trigger put all of the logic to delete the records from all 3 tables.
In any of the above cases, you'd just delete the record from table A, and let the cascading and triggers take care of the rest.
The answers already given (Cascading Deletes and Triggers) are great, but you might work in an environment where these are not an option. If so, below is a purely SQL solution. The example is solely concerned with the DELETE syntax. In the real world you'd probably wrap this within a transaction and implement it as a stored procedure.
--
DECLARE #Param_PK_TableA int
SET #Param_PK_TableA = 1500
-------------------------------
-- TABLE C --------------------
DELETE TableC
FROM TableC
INNER JOIN TableB
ON TableB.TableB_ID = TableC.TableB_ID
INNER JOIN TableA
ON TableA.TableA_ID = TableB.TableA_ID
WHERE
(TableA.TableA_ID = #Param_PK_TableA)
-------------------------------
-- TABLE B --------------------
DELETE TableB
FROM TableB
INNER JOIN TableA
ON TableA.TableA_ID = TableB.TableA_ID
WHERE
(TableA.TableA_ID = #Param_PK_TableA)
-------------------------------
-- TABLE A --------------------
DELETE TableA
WHERE
(TableA.TableA_ID = #Param_PK_TableA)
When you create the foreign key relationship for both tables you can specify ON DELETE CASCADE and it will take care of this for you when you delete a record in A.