我们假设我在两个表之间有一个简单的主 - 详细关系(例如OrderHeaders
和OrderDetails
)。如果我想在删除主记录的同时允许删除详细记录,我可以设置删除操作两者之间的关系进行级联。
这很好,表格基本上与数据库中的任何其他内容无关。但是,如果我们在某个时候将订单变成发票,那么很明显,我们不想删除订单或其相关细节。
在我的OrderHeaders
表中,我添加了一个简单的布尔列(Posted
),如果订单已转换为发票,则设置为true。
如果OrderHeaders
的值等于true,SQL在Posted
表上强加规则以防止删除记录的正确方法是什么?
如果事实上对这种特殊情况有更好的方法,那么我对建议完全开放,学习新东西总是好的。
答案 0 :(得分:0)
没有条件CASCADE。要处理条件删除,只需定义一个手动删除子行的触发器。 (见SQL Server using triggers and geting rid of ON DELETE CASCADE)
答案 1 :(得分:0)
您是如何创建发票的?我可以告诉您,例如,SAP只是将订单标题及其所有详细信息复制到发票和发票详细信息中,因此您可以对订单执行任何操作,但发票保持不变。就个人而言,我不认为这是一个很好的解决方案,因为它基本上保留了相同的数据两次(实际上,如果我没记错的话,它在sap中是最糟糕的 - 数据是被复制2,3,4甚至5次)。
一个相当简单的解决方案是使用不是级联删除的外键将发票连接到订单详细信息表。这样,当您创建发票时,将其链接到订单详细信息。一旦不能从订单详细信息表中删除记录,就不能从订单表中删除它的父记录。
我在rextester上编写了一个简单的演示来向您展示我的意思 - 这是完整的示例(如果rextester不可用):
CREATE TABLE OrderHeader
(
id int identity(1,1) primary key,
createDate datetime
);
CREATE TABLE Invoice
(
id int identity(1,1) primary key,
createDate datetime,
OrderId int foreign key references OrderHeader(id)
);
CREATE TABLE OrderDetails
(
id int identity(1,1) primary key,
orderId int foreign key references OrderHeader(id) on delete cascade,
itemId int,
invoiceId int NULL foreign key references Invoice(id)
);
INSERT INTO OrderHeader (createDate) VALUES (DATEADD(DAY, -1, GETDATE())), (GETDATE());
INSERT INTO OrderDetails (orderId, itemId) VALUES (1, 1), (2, 2);
BEGIN TRANSACTION
INSERT INTO Invoice (createDate, OrderId) VALUES (GETDATE(), 1) ;
UPDATE OrderDetails
SET invoiceId = scope_identity()
WHERE OrderId = 1;
COMMIT TRANSACTION
SELECT 'Headers', * FROM OrderHeader;
SELECT 'Details', * FROM OrderDetails;
SELECT 'Invoice', * FROM Invoice;
DELETE FROM OrderHeader WHERE Id = 2;
SELECT 'Headers', * FROM OrderHeader;
SELECT 'Details', * FROM OrderDetails;
SELECT 'Invoice', * FROM Invoice;
DELETE FROM OrderHeader WHERE Id = 1;