Data purge on Connected Tables – Part 2 (With Filter)
This is an advanced version of the script shared last month. Here we could provide a where clause for the parent table to cascade to all dependent tables through joins. This script was prepared for one of my friend and tested only on NorthWind database. Feel free to test and respond in comments if you face any issues.
set nocount on
IF OBJECT_ID('tempdb.dbo.#fkeys') IS NOT NULL
begin
drop table #fkeys
end
create table #fkeys (id int identity(1,1),
childTableSchema sysname,
childTable sysname,
childKeyName sysname,
childColumnName sysname,
parentTableSchema sysname,
parentTable sysname,
parentColumnName sysname default NULL NULL,
tLevel int default 0,
done bit default 0)
declare @tableSchema sysname, @tableName sysname, @pTableSchema sysname, @pTableName sysname,
@pTableColumnName sysname, @filter varchar(8000),
@pTableKeyName sysname, @tLevel int = 0
set @TableSchema = 'Person' -- TABLE SCHEMANAME
set @TableName = 'Person' -- TABLE NAME
set @filter = 'BusinessEntityID between 20000 and 20777' -- Filter to be applied on table above.
set @pTableSchema = @tableSchema
set @pTableName = @tableName
insert into #fkeys(childTableSchema, childTable, childKeyName, childColumnName, parentTableSchema, parentTable)
select @pTableSchema, @pTableName, ind.name, cl.name, 0, 0 from sys.indexes ind join sys.index_columns icl on ind.object_id = icl.object_id
and ind.index_id = icl.index_id
join sys.columns cl on icl.column_id = cl.column_id and cl.object_id = icl.object_id
where is_primary_key = 1 and ind.object_id = object_id(@pTableSchema+'.'+@pTableName)
--select * from #fkeys
declare @pTableId int
while ((select count(1) from #fkeys where done = 0)>0)
begin
select top 1 @pTableId = id, @pTableSchema= childTableSchema, @pTableName = childTable, @pTableKeyName = childKeyName
, @pTableColumnName = childColumnName, @tLevel = tLevel
from #fkeys where done = 0
insert into #fkeys (childTableSchema, childTable, childKeyName, childColumnName, parentTableSchema, parentTable, parentColumnName, tLevel)
-- childTableSchema, childTable, childKeyName, childColumnName, parentTable
SELECT
schema_name(fk.schema_id) childTableSchema,
object_name(fk.parent_object_id) childTable,
fk.name childKeyName,
cl.name childColumnName,
@pTableSchema parentTableSchema,
@pTableName parentTable,
(select name from sys.columns where column_id = fkc.constraint_column_id and object_id = fkc.referenced_object_id) as parentColumnName,
(@tLevel+1) currentLevel
FROM
sys.tables parent
join
sys.foreign_keys fk
on fk.referenced_object_id = parent.object_id
join sys.foreign_key_columns fkc
on fkc.parent_object_id = fk.parent_object_id
and fk.object_id = fkc.constraint_object_id
join sys.columns cl
on cl.object_id = fk.parent_object_id
and cl.column_id = fkc.parent_column_id
and fkc.referenced_object_id = parent.object_id
where parent.name = @pTableName
update #fkeys set done = 1 where childTableSchema = @pTableSchema and childTable = @pTableName
end
--select * from #fkeys
/*
select * from Person.Address where AddressID between 29859 and 32521
select * from
Sales.SalesOrderDetail SalesOrderDetail_2 join Sales.SalesOrderHeader SalesOrderHeader_1
on SalesOrderDetail_2.SalesOrderID = SalesOrderHeader_1.SalesOrderID
join Person.Address Address_0
on SalesOrderHeader_1.BillToAddressID = Address_0.AddressID and SalesOrderHeader_1.ShipToAddressID = Address_0.AddressID
where Address_0.AddressID between 29859 and 32521
select * from
Sales.SalesOrderHeaderSalesReason SalesOrderHeaderSalesReason_2 join Sales.SalesOrderHeader SalesOrderHeader_1
on SalesOrderHeaderSalesReason_2.SalesOrderID = SalesOrderHeader_1.SalesOrderID
join Person.Address Address_0
on SalesOrderHeader_1.BillToAddressID = Address_0.AddressID and SalesOrderHeader_1.ShipToAddressID = Address_0.AddressID
where Address_0.AddressID between 29859 and 32521
*/
--select * from #fkeys
--
-- GENERATOR START
--
/*
select * from #fkeys
select 'select * from '+fk.childTableSchema+'.'+fk.childTable+' as '+fk.childTable+'_'+convert(varchar,fk.tLevel)+'_'+convert(varchar,fk.id)
,' join '+fk.parentTableSchema+'.'+fk.parentTable+' as '+fk.parentTable+'_'+convert(varchar,pfk.tLevel)+'_'+convert(varchar,fk.id)
+' on '+fk.childTable+'_'+convert(varchar,fk.tLevel)+'_'+convert(varchar,fk.id)+'.'+fk.childColumnName+'='+fk.parentTable+'_'+convert(varchar,pfk.tLevel)+'_'+convert(varchar,fk.id)+'.'+fk.parentColumnName
,* from #fkeys fk
join #fkeys pfk on fk.parentTable = pfk.childTable
and pfk.tLevel < fk.tLevel
order by fk.id desc, fk.tLevel desc, pfk.id desc, pfk.tLevel desc
*/
IF OBJECT_ID('tempdb.dbo.#qTable') IS NOT NULL
begin
drop table #qTable
end
create table #qTable
(
qid int identity (1,1),
headQ varchar(max),
joinQ varchar(max),
fkid int,
childSchema sysname,childTable sysname,childColumn sysname,childLevel int,
parentSchema sysname,parentTable sysname,parentColumn sysname,parentLevel int,
hDone int default 0, jDone int default 0
)
insert into #qTable (headQ, joinQ, fkid, childSchema, childTable, childColumn, childLevel,
parentSchema, parentTable, parentColumn, parentLevel)
select 'delete '+fk.childTable+'_'+convert(varchar,fk.tLevel)+'_'+convert(varchar,fk.id)+' from '+fk.childTableSchema+'.'+fk.childTable+' as '+fk.childTable+'_'+convert(varchar,fk.tLevel)+'_'+convert(varchar,fk.id)
,' join '+fk.parentTableSchema+'.'+fk.parentTable+' as '+fk.parentTable+'_'+convert(varchar,pfk.tLevel)+'_'+convert(varchar,fk.id)
+' on '+fk.childTable+'_'+convert(varchar,fk.tLevel)+'_'+convert(varchar,fk.id)+'.'+fk.childColumnName+'='+fk.parentTable+'_'+convert(varchar,pfk.tLevel)+'_'+convert(varchar,fk.id)+'.'+fk.parentColumnName
,fk.id,fk.childTableSchema,fk.childTable, fk.childColumnName,fk.tLevel, fk.parentTableSchema, fk.parentTable, fk.parentColumnName,
pfk.tLevel
from #fkeys fk
join #fkeys pfk on fk.parentTable = pfk.childTable
and pfk.tLevel < fk.tLevel
order by fk.id desc, fk.tLevel desc, pfk.id desc, pfk.tLevel desc
select * from #qTable
declare @childSchema sysname, @childTable sysname, @childLevel sysname,
@childColumn sysname, @parentColumn sysname,
@parentSchema sysname, @parentTable sysname, @parentLevel sysname, @cmdText varchar(8000), @fkid int, @jid int,
@headQ varchar(8000), @joinQ varchar(8000), @alias1 int, @alias2 int
update #qTable set hDone = 0
WHILE((select count(1) from #qTable where hDone = 0)>0)
BEGIN
select top 1 @headQ=headQ, @joinQ = joinQ, @parentSchema = parentSchema, @parentTable=parentTable, @parentLevel=parentLevel,
@fkid = fkid, @jid = fkid from #qTable where hDone = 0
print @headQ + @joinQ
update #qTable set hDone = 1 where headQ = @headQ
update #qTable set jDone=0
IF OBJECT_ID('tempdb.dbo.#jParentQueue') IS NOT NULL
begin
drop table #jParentQueue
end
create table #jParentQueue (id int identity(1,1), parentSchema sysname, parentTable sysname, parentLevel int, fkid int, done int)
WHILE(
(select count(1) from #qTable where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel)>0
or
(select count(1) from #jParentQueue where done = 0) > 0
)
BEGIN
if((select count(1) from #qTable where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel)>0)
begin
select top 1 @joinQ=replace(joinQ,childTable+'_'+convert(varchar,childLevel)+'_'+convert(varchar,fkid),childTable+'_'+convert(varchar,childLevel)+'_'+convert(varchar,@fkid))
,@jid = fkid
from #qTable where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
print @joinQ
insert into #jParentQueue(parentSchema,parentTable,parentLevel,fkid,done) select parentSchema, parentTable, parentLevel, fkid, 0 from #qTable
where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
order by parentLevel desc
update #jParentQueue set done = 1 where parentLevel = 0
update #qTable set jDone = 1 where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
end
else
begin
select top 1 @parentSchema = parentSchema, @parentTable = parentTable, @parentLevel = parentLevel from #jParentQueue where done = 0
order by parentLevel desc
select top 1 @alias1 = parentLevel, @alias2 = fkid from #jParentQueue where parentSchema = @parentSchema and parentTable = @parentTable
select top 1 @joinQ=replace(joinQ,childTable+'_'+convert(varchar,childLevel)+'_'+convert(varchar,fkid),childTable+'_'+convert(varchar,@alias1)+'_'+convert(varchar,@alias2))
,@jid = fkid
from #qTable where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
print @joinQ+'--FromQueue'
--select distinct parentSchema, parentTable, parentLevel, fkid, 0 from #qTable
-- where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
insert into #jParentQueue(parentSchema,parentTable,parentLevel,fkid,done) select parentSchema, parentTable, parentLevel, fkid, 0 from #qTable
where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
order by parentLevel desc
update #jParentQueue set done = 1 where parentLevel = 0
update #qTable set jDone = 1 where jDone = 0 and childSchema=@parentSchema and childTable=@parentTable and childLevel = @parentLevel
update #jParentQueue set done = 1 where parentSchema = @parentSchema and parentTable = @parentTable and parentLevel = @parentLevel
end
--select * from #jParentQueue
END
print 'where '+@TableName+'_0_'+convert(varchar,@jid)+'.'+@filter
END
--
-- GENERATOR END
--
set nocount off