我必须编写一个删除脚本来从数据库表中删除行。但是,该表有很多子表(外键),那些子表也有子表。
所有关系都有外键,我想使用这些信息以正确的顺序获取我必须删除的表列表(首先是叶表,然后是依赖图)。 / p>
如何以正确的顺序获取给定表的子表列表?
答案 0 :(得分:5)
在您的数据库上尝试此操作,此脚本一次只会为您提供一个表的图表。我假设您有一个Employee表,但您必须更改第2行以检查数据库的特定表:
DECLARE @masterTableName varchar(1000)
SET @masterTableName = 'Employee'
DECLARE @ScannedTables TABLE( Level int, Name varchar(1000) collate Latin1_General_CI_AS )
DECLARE @currentTableCount INT
DECLARE @previousTableCount INT
DECLARE @level INT
SET @currentTableCount = 0
SET @previousTableCount = -1
SET @level = 0
INSERT INTO @ScannedTables VALUES ( @level, @masterTableName )
WHILE @previousTableCount <> @currentTableCount
BEGIN
SET @previousTableCount = @currentTableCount
INSERT INTO @ScannedTables
SELECT DISTINCT
@level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name
WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN ( SELECT Name FROM @ScannedTables WHERE Level = @level )
AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN ( SELECT Name FROM @ScannedTables )
SET @level = @level + 1
SELECT @currentTableCount = COUNT(*) FROM @ScannedTables
END
SELECT * FROM @ScannedTables
答案 1 :(得分:1)
对此没有简单的通用答案,因为表可以递归地依赖于其他表,包括自我关系等。您的结果可能不仅仅是简单的树。
您最好的方法应该取决于您的数据库模型:如果您连接了树表,那么首先从第三个表中删除您的数据,而不是从第三个表中删除数据。
...或禁用约束,删除数据,启用约束。
...或将外键更改为DELETE CASCADE
。
这取决于您的数据模型。
答案 2 :(得分:1)
This article很好地了解了如何做你要问的事情。
编辑:我已将链接中给出的原始查询修改为:
不确定为什么编辑器在格式化代码块方面做得很差。
with Fkeys as (
select distinct
OnTable = onTableSchema.name + '.' + OnTable.name
,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name
from
sysforeignkeys fk
inner join sys.objects onTable
on fk.fkeyid = onTable.object_id
inner join sys.objects againstTable
on fk.rkeyid = againstTable.object_id
inner join sys.schemas onTableSchema
on onTable.schema_id = onTableSchema.schema_id
inner join sys.schemas againstTableSchema
on againstTable.schema_id = againstTableSchema.schema_id
where 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name
)
,MyData as (
select
OnTable = s.name + '.' + o.name
,AgainstTable = FKeys.againstTable
from
sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join FKeys
on s.name + '.' + o.name = FKeys.onTable
left join Fkeys fk2
on s.name + '.' + o.name = fk2.AgainstTable
and fk2.OnTable = Fkeys.AgainstTable
where 1=1
and o.type = 'U'
and o.name not like 'sys%'
and fk2.OnTable is null
)
,MyRecursion as (
-- base case
select
TableName = OnTable
,Lvl = 1
from
MyData
where 1=1
and AgainstTable is null
-- recursive case
union all select
TableName = OnTable
,Lvl = r.Lvl + 1
from
MyData d
inner join MyRecursion r
on d.AgainstTable = r.TableName
)
select
Lvl = max(Lvl)
,TableName
,strSql = 'delete from [' + tablename + ']'
from
MyRecursion
group by
TableName
order by
1 desc
,2 desc