简化 SQL Server 触发器逻辑

Roy*_*mir 6 trigger sql-server sql-server-2008-r2

我有一张Images表,该表包含这些基本列 —

ImageId (int)
VotesUp (int)
VotesDown (int)
Run Code Online (Sandbox Code Playgroud)

每张图片都可以得到赞成票(向上、向下、收回投票)

upvotes 表有:

ImageId (int)
UserId (int)
Score (int )   [-1,0,+1]
Run Code Online (Sandbox Code Playgroud)

因此,如果 -

  • 用户插入 +1 分数然后(如果不存在)我向upvotes表中添加一个新行并更新表中的值Images(用于快速获取)

  • 用户将其分数从 +1 更改为 0,然后降低 votes up

  • 用户将其分数从 +1 更改为 -1,然后减少votes up和增加 votes down
  • 用户将其分数从 0 更改为 +1,然后增加 votes up
  • 用户将其分数从 0 更改为 -1,然后增加 votes down

  • 用户将其分数从 -1 更改为 0 然后降低 votes down

  • 用户将其分数从 -1 更改为 +1,然后减少votes down 和增加votes up

这是一个非常简单的逻辑。

这是upvotes桌子上的触发器:

alter TRIGGER [dbo].[UpvotesChanged]
ON [dbo].[Upvotes]
FOR INSERT, UPDATE
AS
BEGIN
IF EXISTS( SELECT 1  FROM   DELETED ) --update
BEGIN

UPDATE imgs
SET    VotesUp = CASE

                    WHEN deleted.Score = 1 AND INSERTED.score =0      THEN ISNULL(VotesUp, 0) -1
                    WHEN deleted.Score = 1 AND INSERTED.score =-1      THEN ISNULL(VotesUp, 0) -1
                    WHEN deleted.Score = 0 AND INSERTED.score =1      THEN ISNULL(VotesUp, 0) +1
                    WHEN deleted.Score = -1 AND INSERTED.score =1      THEN ISNULL(VotesUp, 0) +1
                    ELSE ISNULL(VotesUp, 0)
                END  
                ,
    VotesDown = CASE 

                    WHEN deleted.Score = 0 AND INSERTED.score =-1      THEN ISNULL(VotesDown, 0) +1
                    WHEN deleted.Score = 1 AND INSERTED.score =-1      THEN ISNULL(VotesDown, 0) +1
                    WHEN deleted.Score = -1 AND INSERTED.score =1      THEN ISNULL(VotesDown, 0) -1
                    WHEN deleted.Score = -1 AND INSERTED.score =0      THEN ISNULL(VotesDown, 0) -1
                    ELSE ISNULL(VotesDown, 0)
                END   
FROM   Images imgs
    JOIN DELETED 
        ON  imgs.ImageId = deleted.ImageId
        JOIN INSERTED ON imgs.ImageId = INSERTED.ImageId


END
ELSE
--insert
BEGIN
UPDATE imgs
SET    VotesUp = CASE 
                    WHEN INSERTED.Score = 1 
                    THEN ISNULL(VotesUp, 0) + 1
                    ELSE VotesUp
                END,
    VotesDown = CASE 
                    WHEN INSERTED.Score = -1 
                        THEN ISNULL(VotesDown, 0) + 1
                    ELSE VotesDown
                END
FROM   Images imgs
    JOIN INSERTED
        ON  imgs.ImageId = INSERTED.ImageId
END
END
Run Code Online (Sandbox Code Playgroud)

它按预期工作。

那么问题出在哪里呢?

题 :

我认为这个触发器可以在没有所有这些 if/cases 的情况下简化。我只是看不到简化。

是否可以简化此触发器?

Pau*_*ite 7

我不使用触发器,而是使用索引视图。

这样,SQL Server 自动保持数据的两个视图同步,无需编写任何触发代码。您也不必担心微妙的竞争条件和其他潜在的并发问题。

索引视图

CREATE TABLE dbo.Upvotes
(
    ImageId integer NOT NULL,
    UserId integer NOT NULL,
    Score smallint NOT NULL,

        CONSTRAINT ValidScore
        CHECK (Score IN (-1, 0, +1)),

        CONSTRAINT PK_dbo_Upvotes__ImageId_UserId
        PRIMARY KEY (ImageId, UserId)
);
GO
CREATE VIEW dbo.Images
WITH SCHEMABINDING
AS
SELECT
    U.ImageId,
    VotesUp = SUM(CASE WHEN U.Score = +1 THEN 1 ELSE 0 END),
    VotesDown = SUM(CASE WHEN U.Score = -1 THEN 1 ELSE 0 END),
    NumRows = COUNT_BIG(*)
FROM dbo.Upvotes AS U
GROUP BY 
    U.ImageId;
GO
CREATE UNIQUE CLUSTERED INDEX cuq
ON dbo.Images (ImageId);
Run Code Online (Sandbox Code Playgroud)

视图中的查询本质上是一个枢轴。它不是使用PIVOT语法编写的,因为在 SQL Server 索引视图中不允许这样做。

对于 Enterprise/Developer Edition,优化器将根据成本做出使用视图索引的决定,或者扩展视图并访问基表。您可以通过NOEXPAND提示强制使用视图索引。

在其他版本中,您必须使用NOEXPAND提示来避免扩展视图。更喜欢使用带有索引视图的提示还有其他原因NOEXPAND

例子

SELECT
    I.ImageId,
    I.VotesUp,
    I.VotesDown
FROM dbo.Images AS I WITH (NOEXPAND);
Run Code Online (Sandbox Code Playgroud)

查看索引访问