Триггер MS SQL Server для обновления рейтинга товара и количества голосов - PullRequest
6 голосов
/ 16 апреля 2009

Чтобы облегчить понимание, я представлю ту же самую проблему, как если бы она была о форуме (само приложение вообще не имеет отношения к форумам, но я думаю, что такая параллель проще для большинства из нас понять, что настоящее приложение о чем-то очень специфическом, что большинство программистов не поймут (это приложение предназначено для хардкорных графических дизайнеров)).

Предположим, что существует таблица потоков, в которой хранится информация о каждой теме форума, и таблица потоков, в которой хранятся рейтинги потоков для каждого пользователя (1-5). Для эффективности я решил кэшировать среднее значение рейтинга и количество голосов в таблице потоков, и триггеры звучали как хорошая идея для его обновления (я делал такие вещи в реальном коде приложения, но я думаю, что триггеры стоит попробовать, несмотря на отладочные опасности).

Как вы знаете, MS SQL Server не поддерживает триггер, выполняемый для каждой строки, он должен быть для каждого оператора. Поэтому я попытался определить это так:

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), избирателей (int) и некоторые другие поля, которые не имеют отношения к текущему вопросу. Таблица потоков содержит только идентификатор потока (внешний ключ), идентификатор пользователя (внешний ключ первичного ключа таблицы пользователей) и рейтинг (tinyint между 1 и 5).

Сообщение об ошибке «Неверный синтаксис рядом с ключевым словом« GROUP ».»

Ответы [ 3 ]

2 голосов
/ 16 апреля 2009

Во-первых, я настоятельно рекомендую вам , а не использовать триггеры.

Если вы получаете синтаксическую ошибку, проверьте, сбалансированы ли ваши парены, а также begin / ends. В вашем случае у вас есть end (в конце), но нет начала. Вы можете исправить это, просто удалив end.

Как только вы исправите это, вы, скорее всего, получите еще несколько ошибок, таких как «столбцы x, y, z не в агрегате или в группе по». Это потому, что у вас есть несколько столбцов, которых нет ни в одном. Вам необходимо добавить thread.rating, thread.voters и т. Д. В свою группу или выполнить какое-либо объединение для них.

Все это при условии, что существует несколько записей с одинаковым идентификатором потока (т. Е. Это не первичный ключ). Если это не случай, то какова цель группы?


Edit:

Я в тупике из-за синтаксической ошибки. Я работал над этим с парой связанных подзапросов. Я догадался о вашей структуре таблицы, поэтому измените по мере необходимости и попробуйте это:

--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

Если это то, что вы хотели, тогда мы можем проверить план производительности / выполнения и изменить при необходимости. Возможно, мы сможем заставить его работать с группой.


Альтернативы триггерам

Если вы обновляете данные, которые влияют на оценки только в нескольких выбранных местах, я бы рекомендовал обновить оценки непосредственно там. Включение логики в триггер это хорошо, но создает много проблем (производительность, видимость и т. Д.). Этому может помочь функция.

Учтите это: ваш триггер будет срабатывать каждый раз, когда кто-то касается этой таблицы. Такие вещи, как количество просмотров, даты последнего обновления и т. Д. Будут выполнять этот триггер. В этих случаях вы можете добавить логику для короткого замыкания триггера, но это быстро усложняется.

2 голосов
/ 16 апреля 2009

D'ооо! Я совершенно неправильно понял ваш вопрос и подумал, что вы спрашиваете о MySQL. Моя вина! Я оставлю решение, приведенное ниже, нетронутым и отмечу его как вики сообщества. Может быть, это будет полезно для кого-то с подобной проблемой на MySQL.


Триггеры MySQL выполняются на строку. Также псевдотаблица "inserted" является соглашением Microsoft SQL Server.

MySQL использует псевдотаблицы NEW и OLD в качестве расширений для языка триггера .

Вот решение вашей проблемы:

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
1 голос
/ 16 апреля 2009

Вам может пригодиться следующее чтение:

Введение в триггеры
Википедия: Триггеры БД

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...