为了使这更容易理解,我将提出完全相同的问题,就好像它是关于一个论坛(实际的应用程序根本不涉及论坛,但我认为这样的并行更容易对于我们大多数人来说,实际的应用程序是关于大多数程序员不会理解的非常具体的东西(这是一个针对硬核图形设计师的应用程序)。
假设有一个线程表存储有关每个论坛线程的信息,以及一个存储每个用户的线程评级的线程表(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'附近的语法不正确。”
答案 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使用伪表NEW
和OLD
作为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
同样,您需要UPDATE
和DELETE
的触发器:
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)
您可能会发现以下阅读材料有用: