I have a table that its primary key used in multiple tables.
I want to delete the rows that have no relation FK in other tables.
How to delete all rows from table which has no FK relation?
First you can select like this:
select * from some_table where some_fk_column not in (
select some_column from second_table
)
if you get good result,then
delete from some_table where some_fk_column not in (
select some_column from second_table
)
If you want to do all without checking all other related tables I say a way but you should take care while using it:
loop through your table
delete the record, if any FK is exists then the record will not
delete (use TRY/CATCH blocks)
in this way you do not need to check all fk and tables
Notice: this way assumes that cascade delete is disabled.
Select *
Into #Tmp
From YOUR_TABLE
Declare #Id int
While EXISTS(SELECT * From #Tmp)
Begin
Select Top 1 #Id = Id From #Tmp
BEGIN TRY
DELETE FROM YOUR_TABLE WHERE ID=#ID
END TRY
BEGIN CATCH
END CATCH
Delete FROM #Tmp Where Id = #Id
End
It ain't pretty, but it should work:
select m.ID
from mastertable m
where not exists( select 1 from table1 where fk_id = m.ID )
and not exists( select 1 from table2 where fk_id = m.ID )
and not exists( select 1 from table3 where fk_id = m.ID )
and not exists( select 1 from table4 where fk_id = m.ID )
and so on;
You will do a table scan of the master table (you want to check every row) but as soon as any reference is found in any of the other tables, those scans halt and the master id is rejected. If the FK column in each of the others tables are indexed, those will be seeks. Anything that makes it all the way through is an ID value that is not referenced in any of the tables.
Now just feed the query above to a delete statement and you get rid of all unreferenced rows.
Related
I have a single-column table Ids, which whose column ID is of type uniqueidentifier. I have another table MyTable which has an ID column as well as many other columns. I would like to delete rows from MyTable 1000 at a time, where the ID from MyTable matches an ID in Ids.
WHILE 1 = 1 BEGIN
DELETE t FROM (SELECT TOP 1000 ID FROM Ids) d INNER JOIN MyTable t ON d.ID = t.ID;
IF ##ROWCOUNT < 1 BREAK;
WAITFOR DELAY #sleeptime; -- some time to be determined later
END
This doesn't seem to work though. What should the statement actually be?
Try this:
DECLARE #BatchSize INT
SET #BatchSize = 100000
WHILE #BatchSize <> 0
BEGIN
DELETE TOP (#BatchSize) t
FROM [MyTable] t
INNER JOIN [Ids] d ON d.ID=t.ID
WHERE ????
SET #BatchSize = ##rowcount
END
Has the benefit that the only variable you need to create is the size, as it uses it for the WHILE loop check. When the delete gets below 100000, it will set the variable to that number, on the next pass there will be nothing to delete and the rowcount will be 0... and so you exit. Clean, simple, and easy to understand. Never use a CURSOR when WHILE will do the trick!
Try
Delete from MyTable
Where ID in
(select top 1000 t.ID
from Ids t inner
join MyTable d on d.Id = t.Id)
You could also try:
set rowcount 1000
delete from mytable where id in (select id from ids)
set rowcount 0 --reset it when you are done.
http://msdn.microsoft.com/en-us/library/ms188774.aspx
WHILE EXISTS (SELECT TOP 1 * FROM MyTable mt JOIN IDs i ON mt.ID = t.ID)
BEGIN
DELETE TOP (1000) FROM MyTable
FROM MyTable mt JOIN IDS i ON mt.ID = i.ID
--you can wait if you want, but probably not necessary
END
--Sorry for the quick post; was in a hurry :)
The DELETE statement in SQL Server supports two FROM clauses; the first FROM identifies the table that is having rows deleted, and the second FROM clause is used for JOINS.
See: http://msdn.microsoft.com/en-us/library/ms189835.aspx
This is for a MS SQL 2005 server. I have a query that is deleting all orphaned records in a table. However, this table has self-referencing FKs. I need to delete these as well, but am unclear how to do so. The current script deletes all records that not appear as FKs in other tables, but i didn't take into account the self-referencing FKs in its own table. the table is simple:
PK, FK, DAta
1, NULL, jibberjab
2, 1, jibberjab2
3, 1, skdfasfa
If you are going to have multiple levels, you can use a common table expression to get the IDs you need to delete and take care of the orphans and all their descendants in one statement:
WITH cte AS (
SELECT pk
FROM myTable
WHERE id = 1 --pk to delete
UNION ALL
SELECT t.pk
FROM myTable t
JOIN cte c
ON t.fk = c.pk
)
DELETE t
FROM cte c
JOIN myTable t
ON c.pk = t.pk
It's probably simplest to do this in two steps, first delete the orphaned records, then to delete the referenced children now orphaned after the first delete:
DELETE FROM TABLE1
WHERE NOT EXISTS (SELECT 1 FROM TABLE2 WHERE TABLE2.FK = TABLE1.PK)
DELETE FROM TABLE1
WHERE NOT EXISTS (SELECT 1 FROM TABLE1 B WHERE B.FK = TABLE1.PK)
Probably you can configure your constraint to be deleted on parent item deletion:
ALTER TABLE table1 WITH CHECK ADD CONSTRAINT FK_table1_table2 FOREIGN KEY(column1)
REFERENCES table2 (ID) ON DELETE SET NULL -- here
ALTER TABLE table1 CHECK CONSTRAINT FK_table1_table2
GO
You are trying to removing records while maintaining self referential integrity. I would advise
drop the constraint
then write the query to delete records which are related using
join.
Create a trigger (for delete)
CREATE TRIGGER ON ExTable
FOR DELETE
AS
IF EXISTS (SELECT * FROM ExTable AS tbl
JOIN DELETED AS del ON del.FK=tbl1.PK)
DELETE FROM ExTable
FROM ExTable ex JOIN DELETED del ON
ex.FK=del.ID
I've got a table in a testing DB that someone apparently got a little too trigger-happy on when running INSERT scripts to set it up. The schema looks like this:
ID UNIQUEIDENTIFIER
TYPE_INT SMALLINT
SYSTEM_VALUE SMALLINT
NAME VARCHAR
MAPPED_VALUE VARCHAR
It's supposed to have a few dozen rows. It has about 200,000, most of which are duplicates in which TYPE_INT, SYSTEM_VALUE, NAME and MAPPED_VALUE are all identical and ID is not.
Now, I could probably make a script to clean this up that creates a temporary table in memory, uses INSERT .. SELECT DISTINCT to grab all the unique values, TRUNCATE the original table and then copy everything back. But is there a simpler way to do it, like a DELETE query with something special in the WHERE clause?
You don't give your table name but I think something like this should work. Just leaving the record which happens to have the lowest ID. You might want to test with the ROLLBACK in first!
BEGIN TRAN
DELETE <table_name>
FROM <table_name> T1
WHERE EXISTS(
SELECT * FROM <table_name> T2
WHERE
T1.TYPE_INT = T2.TYPE_INT AND
T1.SYSTEM_VALUE = T2.SYSTEM_VALUE AND
T1.NAME = T2.NAME AND
T1.MAPPED_VALUE = T2.MAPPED_VALUE AND
T2.ID > T1.ID
)
SELECT * FROM <table_name>
ROLLBACK
here is a great article on that: Deleting duplicates, which basically uses this pattern:
WITH q AS
(
SELECT d.*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY value) AS rn
FROM t_duplicate d
)
DELETE
FROM q
WHERE rn > 1
SELECT *
FROM t_duplicate
WITH Duplicates(ID , TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE )
AS
(
SELECT Min(Id) ID TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE
FROM T1
GROUP BY TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE
HAVING Count(Id) > 1
)
DELETE FROM T1
WHERE ID IN (
SELECT T1.Id
FROM T1
INNER JOIN Duplicates
ON T1.TYPE_INT = Duplicates.TYPE_INT
AND T1.SYSTEM_VALUE = Duplicates.SYSTEM_VALUE
AND T1.NAME = Duplicates.NAME
AND T1.MAPPED_VALUE = Duplicates.MAPPED_VALUE
AND T1.Id <> Duplicates.ID
)
I was trying to determine a better way than so many IF/THEN statements to determine if ONE OR MORE tables contain a ref to a forign key. I simply need to know (true/false) if a row exists in any one of 20 tables. All tables have the same fk field.
I hope this makes sense, if not i'll try to explain further.
I'm not sure I understand the question. Would the following improve your situation?
if exists(select * from Table1 where ForeignKeyColumn = searchValue)
or exists(select * from Table2 where ForeignKeyColumn = searchValue)
or exists(select * from Table3 where ForeignKeyColumn = searchValue)
or ...
Assuming that the 20 tables remains consistant then what about using a left outer join to each of the tables. probably inefficient way of doing it but should work
select Id from
(Select PK.id, isnull(tbl1.fk,0) as fk1, isnull(tbl2.fk,0) as fk2 ... etc
from pk left join tbl1 on pk.id = tbl1.fk left join
tbl2 on pk.id = tbl2.fk ... etc) as VirtualTable
Where fk1>0 or fk2>0 ... etc
So you're trying to work out if you can remove a particular row?
So... perhaps try:
begin tran;
delete tablename
where id = 3;
rollback tran;
Then see what error you get (if any), and how many rows are affected.
What about something like:
if exists (
select * from Table1 where MyKey = #key
union
select * from Table2 where MyKey = #key
union
select * from Table3 where MyKey = #key
...
)
I don't have a SQL server instance open in front of me so I'm sure that's got a syntax error somewhere, but you get the idea =)
In MySQL you can use the syntax
DELETE t1,t2
FROM table1 AS t1
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...
How do I do the same thing in SQL Server?
You can take advantage of the "deleted" pseudo table in this example. Something like:
begin transaction;
declare #deletedIds table ( id int );
delete from t1
output deleted.id into #deletedIds
from table1 as t1
inner join table2 as t2
on t2.id = t1.id
inner join table3 as t3
on t3.id = t2.id;
delete from t2
from table2 as t2
inner join #deletedIds as d
on d.id = t2.id;
delete from t3
from table3 as t3 ...
commit transaction;
Obviously you can do an 'output deleted.' on the second delete as well, if you needed something to join on for the third table.
As a side note, you can also do inserted.* on an insert statement, and both inserted.* and deleted.* on an update statement.
EDIT:
Also, have you considered adding a trigger on table1 to delete from table2 + 3? You'll be inside of an implicit transaction, and will also have the "inserted." and "deleted." pseudo-tables available.
You can use JOIN syntax in FROM clause in DELETE in SQL Server but you still delete from first table only and it's proprietary Transact-SQL extension which is alternative to sub-query.
From example here:
-- Transact-SQL extension
DELETE
FROM Sales.SalesPersonQuotaHistory
FROM Sales.SalesPersonQuotaHistory AS spqh INNER JOIN
Sales.SalesPerson AS sp ON spqh.BusinessEntityID = sp.BusinessEntityID
WHERE sp.SalesYTD > 2500000.00;
You can always set up cascading deletes on the relationships of the tables.
You can encapsulate the multiple deletes in one stored procedure.
You can use a transaction to ensure one unit of work.
Example for delete some records from master table and corresponding records from two detail tables:
BEGIN TRAN
-- create temporary table for deleted IDs
CREATE TABLE #DeleteIds (
Id INT NOT NULL PRIMARY KEY
)
-- save IDs of master table records (you want to delete) to temporary table
INSERT INTO #DeleteIds(Id)
SELECT DISTINCT mt.MasterTableId
FROM MasterTable mt
INNER JOIN ...
WHERE ...
-- delete from first detail table using join syntax
DELETE d
FROM DetailTable_1 D
INNER JOIN #DeleteIds X
ON D.MasterTableId = X.Id
-- delete from second detail table using IN clause
DELETE FROM DetailTable_2
WHERE MasterTableId IN (
SELECT X.Id
FROM #DeleteIds X
)
-- and finally delete from master table
DELETE d
FROM MasterTable D
INNER JOIN #DeleteIds X
ON D.MasterTableId = X.Id
-- do not forget to drop the temp table
DROP TABLE #DeleteIds
COMMIT
Basically, no you have to make three delete statements in a transaction, children first and then parents. Setting up cascading deletes is a good idea if this is not a one-off thing and its existence won't conflict with any existing trigger setup.
Just wondering.. is that really possible in MySQL? it will delete t1 and t2? or I just misunderstood the question.
But if you just want to delete table1 with multiple join conditions, just don't alias the table you want to delete
this:
DELETE t1,t2
FROM table1 AS t1
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...
should be written like this to work in MSSQL:
DELETE table1
FROM table1
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...
to contrast how the other two common RDBMS do a delete operation:
http://mssql-to-postgresql.blogspot.com/2007/12/deleting-duplicates-in-postgresql-ms.html
In SQL server there is no way to delete records from multiple tables using join.
So you have to delete from child first before delete form parent.
This is an alternative way of deleting records without leaving orphans.
Declare #user Table(keyValue int , someString varchar(10))
insert into #user
values(1,'1 value')
insert into #user
values(2,'2 value')
insert into #user
values(3,'3 value')
Declare #password Table( keyValue int , details varchar(10))
insert into #password
values(1,'1 Password')
insert into #password
values(2,'2 Password')
insert into #password
values(3,'3 Password')
--before deletion
select * from #password a inner join #user b
on a.keyvalue = b.keyvalue
select * into #deletedID from #user where keyvalue=1 -- this works like the output example
delete #user where keyvalue =1
delete #password where keyvalue in (select keyvalue from #deletedid)
--After deletion--
select * from #password a inner join #user b
on a.keyvalue = b.keyvalue
All has been pointed out. Just use either DELETE ON CASCADE on the parent table or delete from the child-table and then parent.
As Aaron has already pointed out, you can set delete behaviour to CASCADE and that will delete children records when a parent record is deleted. Unless you want some sort of other magic to happen (in which case points 2, 3 of Aaron's reply would be useful), I don't see why would you need to delete with inner joins.
To build upon John Gibb's answer, for deleting a set of data in two tables with a FK relationship:
--*** To delete from tblMain which JOINs to (has a FK of) tblReferredTo's PK
-- i.e. ON tblMain.Refer_FK = tblReferredTo.ID
--*** !!! If you're CERTAIN that no other rows anywhere also refer to the
-- specific rows in tblReferredTo !!!
BEGIN TRAN;
--*** Keep the ID's from tblReferredTo when we DELETE from tblMain
DECLARE #tblDeletedRefs TABLE ( ID INT );
--*** DELETE from the referring table first
DELETE FROM tblMain
OUTPUT DELETED.Refer_FK INTO #tblDeletedRefs -- doesn't matter that this isn't DISTINCT, the following DELETE still works.
WHERE ..... -- be careful if filtering, what if other rows
-- in tblMain (or elsewhere) also point to the tblReferredTo rows?
--*** Now we can remove the referred to rows, even though tblMain no longer refers to them.
DELETE tblReferredTo
FROM tblReferredTo INNER JOIN #tblDeletedRefs Removed
ON tblReferredTo.ID = Removed.ID;
COMMIT TRAN;
DELETE TABLE1 LIN
FROM TABLE1 LIN
INNER JOIN TABLE2 LCS ON CONDITION
WHERE CONDITION
$sql="DELETE FROM basic_tbl,education_tbl,
personal_tbl ,address_tbl,department_tbl
USING
basic_tbl,education_tbl,
personal_tbl ,address_tbl,department_tbl
WHERE
b_id=e_id=p_id=a_id=d_id='".$id."'
";
$rs=mysqli_query($con,$sql);