首页 > 解决方案 > 删除记录时如何检查外键约束是否失败

问题描述

我在 SQL Server 中创建了一个数据库,前端是 PHP - CodeIgniter。在数据库中,我用其他表创建了多个外键。现在,当用户尝试删除记录时,而不是真正删除我想将记录标记为deleted = 1,只有在子表中没有可用的参考记录时才应该这样做。以下是示例表:

Parent_Table
Id INT(PK), Name Varchar, deleted INT

Child_Table
Id INT(PK), FK_Parent_Table_ID INT, address varchar, deleted INT

以上只是我的表格的示例。现在,每当用户尝试从父表中删除记录时,外键都会检查约束,然后删除该记录,这里我希望将其标记为deleted = 1.

我尝试过使用transaction->starttransaction->complete所以如果外键失败,事务就会中止,但这里的问题是,如果外键没有失败,那么将发生回滚,在这种情况下PRIMARY KEY,记录的内容将被更改,不应进行。

所以,我想要一种在事务开始之前检查外键冲突而不实际删除记录的方法

标签: sqlsql-servercodeignitersql-server-2005

解决方案


要实现您的要求,只需检查子表中是否存在记录,例如

declare @RecordToDelete int = 123;

-- Delete the record if no child records exist
delete
from Parent_Table
where id = @RecordToDelete
and not exists (select 1 from Child_Table where FK_Parent_Table_ID = @RecordToDelete);

-- Flag the record as deleted if child records exist
update Parent_Table set
    Deleted = 1
where id = @RecordToDelete
and exists (select 1 from Child_Table where FK_Parent_Table_ID = @RecordToDelete);

取决于您是否真的需要保留记录,因为您始终可以使用级联删除创建外键。

对于 15 个以上的子表,我会认真考虑始终将记录标记为已删除,而从不打扰实际删除那些没有子记录的记录。一些额外的记录不太可能对您的数据库产生太大影响。


实际上,根据我的经验,子表分为两类:

  • 可以使用级联删除自动删除的那些
  • 那些应该阻止我们删除父记录的

如果是这种情况,所需的检查应该变得更易于管理。


同样对于这些情况,我建议将删除逻辑封装在存储过程中,以便将其全部保存在一个地方,并且如果将来数据库方案发生更改,则易于修改。


注意:就我个人而言,我会将Deleted列设为 abit而不是 a,int因为它更准确地反映了意图。


推荐阅读