使用触发器实现参照完整性操作 (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 joindeleted 表将确保您只删除 OrderNumber 与已删除的记录相同的记录 order_item 表中的记录。 left joinorder_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:。取而代之的是表格,inserteddeleted

如果我理解正确,您想在删除语句中从订单中删除一项。然后,如果最后一项已被删除,您想删除订单本身。我认为以下是这样做的:

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)的主要内容,如果未能解决你的问题,请参考以下文章

SQL语句(二十一)—— 触发器(DML触发器)

SQL Server基础操作(此随笔仅作为本人学习进度记录十 !--触发器)

在SQLServer使用触发器实现数据完整性

存储过程的触发器

触发器与存储过程

SQL Serve 日志体系结构