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

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.