如果任何行通过 FK 引用要删除的行,则无法删除该行。
是否有可能在执行 DELETE 语句之前知道是否有任何行正在引用要删除的行?
如果任何行通过 FK 引用要删除的行,则无法删除该行。
是否有可能在执行 DELETE 语句之前知道是否有任何行正在引用要删除的行?
此脚本将显示所有包含引用您尝试删除的行的行的表:
declare @RowId int = 1
declare @TableName sysname = 'ParentTable'
declare @Command varchar(max)
select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) +
''' where exists(select * from ' + object_name(parent_object_id) + ' where ' + col.name+ ' = ' + cast(@RowId as varchar) + ')'
from sys.foreign_key_columns fkc
join sys.columns col on
fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
where object_name(referenced_object_id) = @TableName
execute (@Command)
假设外键不是复合的。
选项 1(检测):
您执行 aSelect Statement
以确定是否有任何记录正在引用要删除的记录 -如果您愿意,可以手动删除那些引用它的记录。这也可以使用触发器来完成,尽管我建议不要使用触发器,因为它们往往会让人们(和你自己)在路上感到惊讶。
选项 2(自动化):
您可以查看级联删除,如果配置正确,将导致引用要删除的记录的所有记录也被删除。
何时使用级联删除(由Joel Coehoorn撰写的文字解释)
这是关于 stackoverflow上级联删除的精彩讨论。
没有人提到它,但只是为了记录,我使用了很多程序
sp_helpconstraint 'dbo.mytable'
为了找到所有与 dbo.mytable 相关的约束,以及哪些表引用了 dbo.mytable。我觉得它非常有用和方便。
我改进了 Alex Aza 的解决方案。
我使用softdelete,所以有必要在条件“where”中添加一列“delete”。我在 TSQL 中创建了一个函数,它发现表表示 NHibernate 中继承的对象时,PK 是来自父表的 FK。
跟随:
declare @RowId int = 4
declare @TableName sysname = 'TABLE'
declare @Command varchar(max)
select @Command = isnull(@Command + ' union all ', '') + 'select ''' + object_name(parent_object_id) +
''' where exists(select * from ' + object_name(parent_object_id) +
CASE
WHEN EXISTS(select object_name(object_id) from sys.columns col where name = 'deleted' and object_id = parent_object_id)
THEN ' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 '
when dbo.ParentIdFromTable(object_name(parent_object_id)) <> ''
then ' inner join ' + dbo.ParentIdFromTable(object_name(parent_object_id)) + ' on id = ' + dbo.PrimaryKey(object_name(parent_object_id))
+' where ' + col.name+ ' = ' + cast(@RowId as varchar) +' and deleted = 0 '
else
' where ' + col.name+ ' = ' + cast(@RowId as varchar)
END
+ ')'
from sys.foreign_key_columns fkc
join sys.columns col on
fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
where object_name(referenced_object_id) = @TableName
PRINT @Command
execute (@Command)
依赖函数:
CREATE FUNCTION dbo.ParentIdFromTable(@Table varchar(255))
RETURNS varchar(255)
AS
BEGIN
declare @tableParent varchar(255) = ''
if exists(select pk.TABLE_NAME, pk.COLUMN_NAME, col.name, object_name(referenced_object_id) Referenced, object_name(parent_object_id) as Parent
from sys.columns col
inner join sys.foreign_key_columns fkc on
fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
AND table_name = @table)
begin
while exists(select *
from sys.columns col
inner join sys.foreign_key_columns fkc on
fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 AND table_name = @table)
begin
-- Descobrir o parent, column
select @tableParent = object_name(referenced_object_id)
from sys.columns col
inner join sys.foreign_key_columns fkc on
fkc.parent_object_id = col.object_id and fkc.parent_column_id = col.column_id
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE pk on
pk.TABLE_NAME = object_name(object_id) and pk.COLUMN_NAME = col.name
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
AND table_name = @table
--print @tableParent
set @table = @tableParent
end
end
return @tableParent;
END;
GO
CREATE FUNCTION dbo.PrimaryKey(@Table varchar(255))
RETURNS varchar(255)
AS
BEGIN
declare @columnName varchar(255) = ''
-- Descobrir o parent, column
select @columnName = COLUMN_NAME
from INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1
AND table_name = @Table
return @columnName
end;