MS SQL Server触发器更新项目评级和投票数

时间:2009-04-15 23:48:39

标签: sql-server triggers

为了使这更容易理解,我将提出完全相同的问题,就好像它是关于一个论坛(实际的应用程序根本不涉及论坛,但我认为这样的并行更容易对于我们大多数人来说,实际的应用程序是关于大多数程序员不会理解的非常具体的东西(这是一个针对硬核图形设计师的应用程序)。

假设有一个线程表存储有关每个论坛线程的信息,以及一个存储每个用户的线程评级的线程表(1-5)。为了提高效率,我决定在线程表中缓存评级平均值和投票数,触发器听起来更像是一个好主意(我曾经在实际应用程序代码中做过这样的事情,但我认为触发器值得一试,尽管调试危险)。

如您所知,MS SQL Server不支持每行执行一次触发器,它必须是per语句。所以我尝试用这种方式定义它:

CREATE TRIGGER thread_rating ON threadrating
AFTER INSERT
AS
    UPDATE thread
    SET 
        thread.rating = (thread.rating * thread.voters + SUM(inserted.rating))/(thread.voters + COUNT(inserted.rating)),
        thread.voters = thread.voters + COUNT(inserted.rating)
    FROM thread
    INNER JOIN inserted ON(inserted.threadid = thread.threadid)
    GROUP BY inserted.threadid

但是我收到了“GROUP BY”子句的错误(我预期)。问题是,我该如何做到这一点?

很抱歉,如果问题很愚蠢,但这是我第一次尝试使用触发器。

附加信息:线程表将包含threadid(int,主键),rating(float),votes(int)以及对当前问题无关的其他一些字段。 该threadrating表只包含threadid(外键),userid(users表主键的外键)和rating(tinyint在1和5之间)。

错误消息是“关键字'GROUP'附近的语法不正确。”

3 个答案:

答案 0 :(得分:2)

首先,我强烈建议你使用触发器。

如果您收到语法错误,请检查您的parens是否与begin / ends一致。在您的情况下,您有一个end(最后),但没有开始。你可以解决这个问题,只需删除end即可。

修复后,您可能会收到更多错误,例如“列x,y,z不在聚合或组中”。那是因为你有几列不在其中。您需要将thread.rating,thread.voters等添加到您的组中,或者对它们执行某种聚合。

这是假设有多个记录具有相同的threadID(即,它不是主键)。如果那个这个案子,那么小组的目的是什么?


编辑:

我对语法错误感到困惑。我使用了几个相关的子查询来解决它。我猜到了你的表结构,所以根据需要进行修改并试试这个:

--CREATE TABLE ThreadRating (threadid int not null, userid int not null, rating int not null)
--CREATE TABLE Thread (threadid int not null, rating int not null, voters int not null)

ALTER TRIGGER thread_rating ON threadrating
AFTER INSERT
AS 

UPDATE Thread
SET Thread.rating = 
    (SELECT (Thread.Rating * Thread.Voters + SUM(I.Rating)) / (Thread.Voters + COUNT(I.Rating))
     FROM ThreadRating I WHERE I.ThreadID = thread.ThreadID)
  ,Thread.Voters = 
    (SELECT Thread.Voters + COUNT(I.Rating) 
     FROM ThreadRating I WHERE I.ThreadID = Thread.ThreadID)                         
FROM Thread
JOIN Inserted ON Inserted.ThreadID = Thread.ThreadID

如果这是您想要的,那么我们可以检查性能/执行计划并根据需要进行修改。我们或许可以让它与该小组合作。


触发器的替代品

如果您只是在几个选定位置更新影响评级的数据,我建议您直接更新评级。将逻辑分解为触发器很好但提供了许多问题(性能,可见性等)。这可以通过函数来​​辅助。

考虑一下:你的触发器会在每个人接触到该表时执行。视图计数,上次更新日期等操作将执行此触发器。在这些情况下,您可以添加逻辑来短路触发器,但它会很快变得复杂。

答案 1 :(得分:2)

D'喔!我完全误解了你的问题,我以为你在问MySQL。 Mea culpa!我将完整保留下面的解决方案,并将其标记为社区维基。也许对MySQL有类似问题的人有用。


每行执行MySQL触发器 。伪表“inserted”也是Microsoft SQL Server约定。

MySQL使用伪表NEWOLD作为trigger language的扩展名。

以下是您问题的解决方案:

CREATE TRIGGER thread_rating 
  AFTER INSERT ON threadrating
  FOR EACH ROW
BEGIN
    UPDATE thread
    SET rating = (rating*voters + NEW.rating)/(voters+1),
        voters = voters + 1
    WHERE threadid = NEW.threadid;
END

同样,您需要UPDATEDELETE的触发器:

CREATE TRIGGER thread_rating 
  AFTER UPDATE ON threadrating
  FOR EACH ROW
BEGIN
    UPDATE thread
    SET rating = (rating*voters - OLD.rating + NEW.rating)/voters,
    WHERE threadid = NEW.threadid;
END

CREATE TRIGGER thread_rating 
  AFTER DELETE ON threadrating
  FOR EACH ROW
BEGIN
    UPDATE thread
    SET rating = (rating*voters - OLD.rating)/(voters-1),
        voters = voters - 1
    WHERE threadid = OLD.threadid;
END

答案 2 :(得分:1)

您可能会发现以下阅读材料有用:

An introduction to Triggers
Wikipedia: DB Triggers