触发器反转所做的更改 - SQL Server

时间:2017-11-01 07:54:05

标签: sql-server triggers

我正在开发一个电子商务系统,我有一个订单表,存储有关订单的所有信息。订单经历了不同的阶段:开放,验证,进行中等。我在不同的阶段保持这些订单的数量,例如未完成订单95,已验证5,正在处理3等

当在表中插入新订单时,我有一个触发器,将未结订单递增1.同样,我有一个更新触发器,它检查订单的前一阶段,然后下一个递减并相应地递增。

如上所述,INSERT触发器正常工作。但是UPDATE触发器有一种奇怪的行为,它会对Counts进行所需的更改,但由于某种原因会反转这些更改。

例如,在将订单状态从“打开”更改为“已验证”时,理想的行为是将“未结订单”减少1并将“验证订单”增加1.触发器当前执行了所需的操作,但由于某种原因,还原以前的价值。

这是我的触发片段,我在此检查订单以前是否属于未结状态,现在正在更新为已验证状态:

IF @@ROWCOUNT = 0 RETURN

DECLARE @orderID VARCHAR(MAX) -- orderID of the order that is being updated
DECLARE @storeID VARCHAR(MAX) -- storeID of the store the order belongs to

SELECT TOP 1
    @orderID = i.id,
    @storeID = i.storeID
FROM
    inserted AS i
    INNER JOIN deleted AS d
        ON i.id = d.id

-- IF from Open Order
IF EXISTS (
    SELECT *
    FROM
        deleted
    WHERE
        orderStatus = 'Open' AND
        id = @orderID
)
BEGIN
    -- IF to Verified Order
    IF EXISTS (
        SELECT *
        FROM
            inserted
        WHERE
            orderStatus = 'Verified' AND
            id = @orderID
    )
    BEGIN
        UPDATE order_counts
        SET 
            open_orders = open_orders - @@ROWCOUNT,
            verified_orders = verified_orders + @@ROWCOUNT
        WHERE storeID = @storeID
    END
END

修改: 这里有一些额外的信息,根据对问题的第一条评论会有所帮助: 我在表中有很多记录,因此一次又一次地使用COUNT()会对整体性能产生很大影响。这就是为什么我把计数保存在一个单独的表中。此外,我以一种处理单个记录/多记录更改的方式编写了触发器。我只检查一行,因为我知道如果有多个记录,他们都会经历相同的状态变化。因此,@@ROWCOUNT

的减量/增量

2 个答案:

答案 0 :(得分:0)

如果你能容忍略微不同的订单计数表示,我强烈建议使用indexed view代替 1

create table dbo.Orders (
    ID int not null,
    OrderStatus varchar(20) not null,
    constraint PK_Orders PRIMARY KEY (ID)
)
go
create view dbo.OrderCounts
with schemabinding
as
    select
        OrderStatus,
        COUNT_BIG(*) as Cnt
    from
        dbo.Orders
    group by OrderStatus
go
create unique clustered index IX_OrderCounts on dbo.OrderCounts (OrderStatus)
go
insert into dbo.Orders (ID,OrderStatus) values
(1,'Open'),
(2,'Open'),
(3,'Verified')
go
update dbo.Orders set OrderStatus = 'Verified' where ID = 2
go
select * from dbo.OrderCounts

结果:

OrderStatus          Cnt
-------------------- --------------------
Open                 1
Verified             2

这样做的好处是,虽然SQL Server正在执行与运行触发器非常相似的操作,但此代码已经过彻底调试并且是正确的。

在当前尝试的触发器中,触发器当前被破坏的另一个原因是@@ROWCOUNT不是“粘性” - 它不记得受原始{{1}影响的行数当你在触发器中运行其他语句时也会设置UPDATE

1 您始终可以在此视图的顶部堆叠非索引视图,如果您真的想要计算单行和多列中的计数,则执行@@ROWCOUNT

答案 1 :(得分:0)

这种行为的原因是多次使用@@ ROWCOUNT,而实际上一旦获取@@ ROWCOUNT的结果,结果就会被清除。而是将结果变为变量并在触发器中使用该变量。检查以下方案是否相同。

CREATE DATABASE Test

USE Test

CREATE TABLE One
(
   ID INT IDENTITY(1,1)
   ,Name NVARCHAR(MAX)
)

GO
CREATE TRIGGER TR_One ON One FOR INSERT,UPDATE
AS
BEGIN

PRINT @@ROWCOUNT

SELECT @@ROWCOUNT

END


UPDATE One
SET Name = 'Name4'
WHERE ID = 3

结果: -

Print @@ ROWCOUNT语句的值为1,其中select @@ ROWCOUNT的值为0