2011-06-10 26 views
14

如果任何行引用要通过FK删除的行,则无法删除行。SQL Server:如何知道是否有行正在引用要删除的行

是否有可能知道是否有任何行在执行DELETE语句之前引用要删除的行?

+0

可能重复http://stackoverflow.com/questions/6244077/help-on-sql -server-trigger) – gbn 2011-06-10 04:46:34

+0

我的目标不是导致级联删除,而是事先知道记录是否可删除。如果它不可删除,则会通知用户它不可删除。 – Yeonho 2011-06-10 06:07:20

+2

我知道你已经标记了一个答案,但是如果这是一个多用户系统,最好的答案可能是试图删除并应对发生的错误 - 否则在这里可能有各种竞争条件,除非您将所有内容都包装在高度隔离的事务中。 – 2011-06-10 06:43:59

回答

18

该脚本将显示所有具有引用该行的行你正在试图删除的表:

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) 

假设外键不是复合。

3

选项1(检测出)

您执行Select Statement,以确定是否有任何记录引用记录要被删除的 - 而且,如果你想,手动删除这些记录是请参考它。这也可以使用触发器完成,尽管我建议不要触发器,因为它们往往会让人们(以及你自己)感到惊讶。

选项2(自动化)

你可以看看Cascading Deletes,如果配置正确,会导致所有记录引用的记录要被删除的也被删除。

当使用级联删除(意译由Joel Coehoorn书面文本)

  • 级联删除可能是有意义的,当关系的语义可以包括一个“是的一部分”的描述。示例:网络订单,网络订单行项目
  • 如果您保留历史记录或使用软删除(只设置删除位列),则不应使用级联删除
  • 如果您设置了自己的级联,级联可能会使您陷入麻烦外键错误。
  • 在彻底理解之前使用级联并不明智。但是,这是一个有用的功能,因此值得花时间理解。

这是关于stackoverflow的Cascading Deletes的一个很好的讨论。

+0

当您尝试使用FK删除记录时,SQL Server会引发异常。 IS SQL Server内部执行SELECT语句以知道是否有任何记录引用它? – Yeonho 2011-06-10 04:42:15

+2

SQL以各种方式跟踪关系和约束,其中一些方法类似于select语句。不过,我认为它更像垃圾收集。 “没有什么东西可以再引用这块内存了,所以我可以删除它” – 2011-06-10 04:44:08

0

没有人提到这个问题,但仅仅是为了记录我用了很多的程序

sp_helpconstraint 'dbo.mytable' 

,以便找到所有dbo.mytable相关的约束,并表参考dbo.mytable。 我觉得它非常有用和方便。

0

我改进了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) 

Depedencies功能:

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; 
[求助上的SQL Server触发器(的
相关问题