同一张表的外键约束可能会导致循环或多个级联路径

时间:2020-09-07 19:25:20

标签: sql sql-server entity-framework

我有一个Transaction(Id,Comment,ClosingTransactionId)表。我希望在删除时实现“设置为空”功能。

所以如果我有两行:

(1,开始交易,2)

(2,完成交易,为空)

,然后删除第二行,然后将第一行的ClosingTransactionId设置为null。我可以使用“无操作”向ClosingTransactionId添加外键约束,但不能通过“ set null”添加外键约束,因为SQL Server会引发“循环或多个级联路径”。

有些页面解释了问题,https://stackoverflow.com/a/12683993/5852947,有些则建议使用触发器,https://stackoverflow.com/a/852047/5852947,另一些则说什么比触发器更好。我应该走哪条路? (当然,我可以将第一行的ClosingTransactionId设置为null,然后删除第二行,但我真的希望数据库能够处理此问题。)

如果有帮助,我会使用EF6。

2 个答案:

答案 0 :(得分:2)

我真的希望Db能够解决这个问题

然后,您需要一个INSTEAD OF DELETE触发器。在许多情况下,SQL Server禁止使用级联删除,而在其他情况下,级联删除是有意义的,部分原因是您始终可以使用触发器来实现该行为。

它们不是邪恶的,只是容易出错。

例如

use tempdb
drop table if exists [transaction]
go
create table [Transaction]
(
  Id int primary key, 
  Comment nvarchar(200), 
  ClosingTransactionId int null references [Transaction]
)

insert into [Transaction]
values (1,'opening transactin',null),(2,'closing transaction',null)

update [Transaction] set ClosingTransactionId = 2 where id = 1


delete from [Transaction] where id = 2
--Msg 547, Level 16, State 0, Line 17
--The DELETE statement conflicted with the SAME TABLE REFERENCE constraint "FK__Transacti__Closi__29572725". The conflict occurred in database "tempdb", table "dbo.Transaction", column 'ClosingTransactionId'.

go

create or alter trigger TransactionDelete  
  on [Transaction]
  instead of delete
as
begin
  set nocount on

  update [Transaction] set ClosingTransactionId = null
  where ClosingTransactionId in (select id from deleted)

  delete from [Transaction] 
  where id in (select id from deleted)

end

go
delete from [Transaction] where id = 2
--(1 row affected)

对于EF,您仍然应该为级联删除配置模型,但是要阻止它尝试在数据库中创建外键。您将无需级联删除就可以创建并添加触发器。

答案 1 :(得分:0)

您可以采用不同的设计来代替自引用FK,以避免出现多个级联路径并避免触发。

下面的SO帖子使我有了这个主意: https://stackoverflow.com/a/3548225/634935

TransactionReferenceTable

+------------------------------------------------------------------------+
|                      TransactionReferenceId (PK)                       |
+------------------------------------------------------------------------+
| 1                                                                      |
| 2  --> Deleting this transaction will lead to child being set as NULL  |
| 3                                                                      |
+------------------------------------------------------------------------+

TransactionDetailTable


+-------------------------+--------------------------+--------------------------------------------------------------+
| TransactionSurrogateKey | OpeningTransactionId(FK) |                  ClosingTransactionId (FK)                   |
+-------------------------+--------------------------+--------------------------------------------------------------+
|                       1 |                        1 | 2 --> On deletion of transaction ref, it will be set as null |
+-------------------------+--------------------------+--------------------------------------------------------------+