【问题标题】:How to DELETE without ON DELETE CASCADE (conflict REFERENCE constraint)如何在没有 ON DELETE CASCADE 的情况下进行 DELETE(冲突 REFERENCE 约束)
【发布时间】:2012-12-06 17:56:39
【问题描述】:

我有一个巨大的遗留数据库,其中一个表有多个指向另一个表的外键,并且看不到一个级联,类似于下面的示例表:

create table Users (
    Id int primary key identity,
    Name varchar(max)
)

create table Products (
    Id int primary key identity,
    Name varchar(max),
    CreatedBy int foreign key references Users(Id),
    UpdatedBy int foreign key references Users(Id)
)

insert into Users values('Bar')
insert into Users values('Baz')
insert into Products values('Foo', 1, 2)

我需要能够删除一些旧数据,但它当然会抛出引用异常:

delete from Users where Name='Bar'

DELETE 语句与 REFERENCE 约束“FK__Products__Create__1AD3FDA4”冲突。冲突发生在数据库“Foo”、表“dbo.Products”、列“CreatedBy”中。


由于数据库的复杂性,我无法预先删除所有引用,因此我尝试以编程方式添加临时外键,并设置级联来解决它们。但是,对于这个具有多个外键到另一个表的特定表,这会导致第二个 UpdatedBy 上的 cycles or multiple cascade paths 改变:

alter table Products add foreign key (CreatedBy) references Users(Id) on delete cascade
alter table Products add foreign key (UpdatedBy) references Users(Id) on delete cascade

在表“Products”上引入 FOREIGN KEY 约束“FK__Products__Update__1DB06A4F”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。


如何让delete from Users where 工作,同时保持参照完整性,无论是通过某种方式解决多个级联路径问题还是其他方式?

【问题讨论】:

  • 考虑以下情况:您想删除ID=3 的用户。有一个由该用户创建并由用户更新为ID=5 的产品。假设您在删除用户3 时将该产品作为级联的一部分删除,那么您需要查询ID=5 更新的所有产品。您将错过一种产品!或者,您可以保留产品并将createdBy 设置为NULL,这样您就不会错过重要信息。但这当然不是级联删除。您需要问自己是否真的要删除该用户,以及是否没有更好的选择。

标签: sql-server tsql cascading-deletes


【解决方案1】:

我个人不会这样做(我会预先删除所有引用的数据并手动检查完整性)。见:Can foreign key constraints be temporarily disabled using T-SQL?

引用:

-- disable all constraints
EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

在禁用约束后删除您的数据,但请记住之后再次打开它们!

-- enable all constraints
EXEC sp_msforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

另请注意,存储过程 sp_msforeachtable 未记录在案,可能会在 SQL Server 的未来版本中消失。

如果您不想全面禁用约束(也许您有一个适用的表列表),那么只需禁用它们,如您在上面的代码中所见。

ALTER TABLE [Products] NOCHECK CONSTRAINT ALL
DELETE FROM [Users] where Name='Bar'
ALTER TABLE [Products] WITH CHECK CHECK CONSTRAINT ALL

所有功劳归kristof's answer。请投票!

【讨论】:

  • 谢谢,正如 OP 中提到的,预删除引用并不是一个真正的选择,db 绝对是巨大的,投入意义资源来逐步浏览数百个表并不是生产功能。我不想禁用所有约束检查,因为我确实希望删除与已删除用户相关的所有垃圾,而不是无参考地闲逛。
猜你喜欢
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
  • 2011-09-21
  • 1970-01-01
  • 2013-04-19
相关资源
最近更新 更多