使用触发器实现参照完整性操作 (SQL Server)
Posted
技术标签:
【中文标题】使用触发器实现参照完整性操作 (SQL Server)【英文标题】:Using triggers to implement referential integrity actions (SQL Server) 【发布时间】:2015-05-06 20:05:49 【问题描述】:我正在尝试为我的表实现一些触发器
其中两个表是ORDERS
(有订单)和ORDERS_ITEMS
(每个订单都有项目),当ORDER
中没有更多ITEMS
时,我想删除ORDER
。
我希望我的触发器看起来像这样
CREATE TRIGGER DELETE_ORDER_WHEN_NO_ITEMS
INSTEAD OF DELETE
ON ORDER
DECLARE rowcount int;
BEGIN
// First detemine if this is the last item in the ORDER
SELECT Count(*)
INTO rowcount
FROM ORDER_ITEM
WHERE ORDER_ITEM.ItemNumber = old:ItemNumber;
// Delete ITEM row
DELETE ORDER_ITEM
WHERE ORDER_ITEM.ItemNumber = old:ItemNumber;
// Last ITEM in ORDER delete the whole ORDER
IF (rowcount = 1) THEN
DELETE ORDER
WHERE ORDER.OrderNumber = old:OrderNumber;
END IF
END;
我不确定如何在 SQL Server 中编写此代码,并且我从书中获得了算法,但无法在 SQL Server 上运行它。
【问题讨论】:
我只是想知道你为什么要在数据库上这样做?这听起来像是一个业务规则,并且在代码中处理起来可能更容易和更易于管理。 请显示您的表格布局。我不清楚为什么名为Order
的表会有一个名为ItemNumber
的列。我希望外键关系位于 OrderId
之类的列上。
【参考方案1】:
不要为此使用触发器。使用外键和cascade delete.
更新
对不起,我跳错了结论。如果您想在没有关联项目的情况下删除订单,级联删除将无济于事。 在 order_item 表上使用 after delete 触发器并在此处编写此删除语句:(再次更新)
CREATE TRIGGER order_item_delete ON order_item
FOR DELETE
AS
DELETE order
FROM order
INNER JOIN deleted ON (order.OrderNumber = deleted.OrderNumber) -- 1
LEFT JOIN order_Item ON(order.OrderNumber = order_item.OrderNumber)
WHERE order_Item.ItemNumber IS NULL -- 2
GO
细分:
inner join
和 deleted
表将确保您只删除 OrderNumber 与已删除的记录相同的记录
order_item 表中的记录。
left join
与 order_item 表以及 where
子句确保您仅在 orders 表中删除没有 order_item的记录> 附在他们身上的记录。
【讨论】:
但我认为级联删除仅在删除父级时才有效,然后所有子级也将被删除,但反之则不行? 我真的不知道把你的代码放在哪里?我需要 IF 条件吗?我的意思是在触发器本身? 查看我编辑的答案。你不需要 if 那里。如果您有很多订单商品,可能会有点慢,但我认为对于 20,000-50,000 条以下的记录,您应该没问题。 我收到这些错误消息 156,级别 15,状态 1,行 404 关键字“选择”附近的语法不正确。消息 102,级别 15,状态 1,第 407 行 ')' 附近的语法不正确。【参考方案2】:您的语法看起来更像 Oracle; SQL Server 语法完全不同。例如,没有new:
和old:
。取而代之的是表格,inserted
和 deleted
。
如果我理解正确,您想在删除语句中从订单中删除一项。然后,如果最后一项已被删除,您想删除订单本身。我认为以下是这样做的:
CREATE TRIGGER trg_orders_delete_one_item ON orders
INSTEAD OF DELETE
AS
begin
with todelete as (
select oi.*, row_number() over (partition by OrderId order by ItemNumber desc) as seqnum
from Order_Items oi
where oi.OrderId in (select OrderId from deleted)
)
delete todelete
where seqnum = 1;
with todelete as (
select o.*
from orders o
where o.OrderId in (select OrderId from deleted)
)
delete todelete
where not exists (select 1
from order_item oi
where oi.OrderId = todelete.OrderId
)
end;
【讨论】:
非常感谢您,查询不起作用,因为它说 ORDER 与 CUSTOMER 有 FK(我没有提到的表)。 非常感谢您的再次回答,我真的很抱歉没有显示我所有的不好的表。以上是关于使用触发器实现参照完整性操作 (SQL Server)的主要内容,如果未能解决你的问题,请参考以下文章