在SQL-Server中处理自引用外键约束的推荐方法是什么?
表 - 型号:
fiData
引用tabData中的上一条记录。如果我删除了fiData
引用的记录,则数据库会抛出异常:
“DELETE语句与SAME TABLE REFERENCE冲突 约束“FK_tabDataPrev_tabDataNext”。冲突发生在 数据库“MyDataBase”,表“dbo.tabData”,列'fiData'“
如果Enforce Foreignkey Constraint
设置为“是”。
我不需要级联删除被引用的记录,但我需要设置引用它的fiData=NULL
。我的想法是将Enforce Foreignkey Constraint
设置为“否”并创建一个删除触发器。这是推荐还是有更好的方法?
谢谢。
答案 0 :(得分:7)
与Andomar不同,我很乐意使用触发器 - 但我不会删除约束检查。如果将其实现为instead of
触发器,则可以在执行实际删除之前将其他行重置为null:
CREATE TRIGGER T_tabData_D
on tabData
instead of delete
as
set nocount on
update tabData set fiData = null where fiData in (select idData from deleted)
delete from tabData where idData in (select idData from deleted)
它简短,简洁,如果SQL Server可以处理同一个表的外键级联(在其他RDBMS中,您可能只能为外键约束指定ON DELETE SET NULL
则没有必要,YMMV)。
答案 1 :(得分:2)
触发器增加了隐含的复杂性。在具有触发器的数据库中,您不会通过查看它来了解SQL语句的作用。根据我的经验,触发器是一个坏主意,没有例外。
在您的示例中,将强制约束设置为“否”意味着您可以添加不存在的ID。并且查询优化器效率较低,因为它不能假定密钥有效。
考虑改为创建存储过程:
create procedure dbo.NukeTabData(
@idData int)
as
begin transaction
update tabData set fiData = null where fiData = @idData
delete from tabData where idData = @idData
commit transaction
go
答案 2 :(得分:0)
这回答很晚。
但对于像我这样搜索的人来说。
并希望cascade
这里有很好的解释
http://devio.wordpress.com/2008/05/23/recursive-delete-in-sql-server/
问题 尽管可以在SQL Server中使用CASCADE DELETE定义外键,但不支持递归级联删除(即在同一个表上进行级联删除)。
如果创建INSTEAD OF DELETE触发器,则此触发器仅触发第一个DELETE语句,并且不会触发从此触发器递归删除的记录。
在MSDN上针对SQL Server 2000和SQL Server 2005记录了此行为。
解决方案 假设您有一个如下定义的表:
CREATE TABLE MyTable (
OID INT, -- primary key
OID_Parent INT, -- recursion
... other columns
)
然后删除触发器如下所示:
CREATE TRIGGER del_MyTable ON MyTable INSTEAD OF DELETE
AS
CREATE TABLE #Table(
OID INT
)
INSERT INTO #Table (OID)
SELECT OID
FROM deleted
DECLARE @c INT
SET @c = 0
WHILE @c <> (SELECT COUNT(OID) FROM #Table) BEGIN
SELECT @c = COUNT(OID) FROM #Table
INSERT INTO #Table (OID)
SELECT MyTable.OID
FROM MyTable
LEFT OUTER JOIN #Table ON MyTable.OID = #Table.OID
WHERE MyTable.OID_Parent IN (SELECT OID FROM #Table)
AND #Table.OID IS NULL
END
DELETE MyTable
FROM MyTable
INNER JOIN #Table ON MyTable.OID = #Table.OID
GO