2009-04-15 18 views
6

Para que esto sea más fácil de entender, presentaré el mismo problema exactamente como si se tratara de un foro (la aplicación real no tiene que ver con foros en Todo, pero creo que ese paralelo es más fácil de entender para la mayoría de nosotros, la aplicación real es algo muy específico que la mayoría de los programadores no entenderán (es una aplicación destinada a diseñadores gráficos hardcore).Disparador de MS SQL Server para actualizar la calificación del artículo y el número de votos

Supongamos que hay una tabla de subprocesos que almacena información sobre cada subproceso de foro y una tabla de subprocesamiento que almacena clasificaciones de subprocesos por usuario (1-5). Por eficiencia, decidí almacenar en caché el promedio de calificación y el número de votos en la tabla de hilos y los disparadores sonaron como una buena idea para actualizarlo (solía hacer esas cosas en el código de la aplicación real, pero creo que los desencadenantes valen la pena intentarlo, a pesar los peligros de depuración).

Como ya sabe, MS SQL Server no admite que se ejecute un disparador por fila, tiene que ser por instrucción. Así que he intentado definir de esta manera:

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 

pero me da un error para el "GROUP BY" cláusula (que yo esperaba). La pregunta es, ¿cómo puedo hacer que esto funcione?

Disculpa si la pregunta es estúpida, pero es la primera vez que intento usar activadores.

Información adicional: La tabla de hilos contendría threadid (int, clave principal), rating (float), votantes (int) y algunos otros campos que son irrelevantes a la pregunta actual. La tabla de subprocesos solo contiene threadid (clave externa), ID de usuario (clave externa a la clave principal de la tabla de usuarios) y calificación (tinyint entre 1 y 5).

El mensaje de error es "Sintaxis incorrecta cerca de la palabra clave 'GRUPO'."

+0

Cuál es la clave principal en la tabla? –

+0

Publicar la tabla DML y el mensaje de error real también ayudaría. –

Respuesta

2

En primer lugar, recomiendo encarecidamente que no disparadores de uso.

Si obtiene un error de sintaxis, compruebe que sus pares estén equilibrados, así como su begin/ends. En su caso, tiene un end (al final) pero no comienza. Puede solucionarlo simplemente eliminando el end.

Una vez que corrija eso, es probable que obtenga algunos errores más, como "columnas x, y, z no en un agregado o grupo por". Eso es porque tiene varias columnas que no están en ninguna de las dos. Necesita agregar thread.rating, thread.voters, etc. a su grupo o realizar algún tipo de agregado en ellos.

Todo esto supone que hay múltiples registros con el mismo threadID (es decir, no es la clave principal). Si eso es no el caso, entonces ¿para qué sirve el grupo?


Editar:

estoy confundido en el error de sintaxis. Trabajé alrededor con un par de consultas secundarias correlacionadas.Supuse a su estructura de la tabla de modo modificar según sea necesario y tratar esto:

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

Si eso es lo que quería, entonces podemos comprobar el plan de actuación/ejecución y modificar según sea necesario. Podríamos lograr que funcione con el grupo todavía.


Alternativas a los disparadores

Si está actualizando los datos que los índices de impacto en sólo unos pocos lugares selectos, me gustaría recomendar la actualización de las calificaciones directamente allí. Factorizar la lógica en un disparador es bueno, pero proporciona muchos problemas (rendimiento, visibilidad, etc.). Esto puede ser ayudado por una función.

Considere esto: su disparador se ejecutará cada vez que alguien toque esa mesa. Cosas como el recuento de vistas, las últimas fechas actualizadas, etc. ejecutarán este disparador. Puede agregar lógica para cortocircuitar el gatillo en esos casos, pero se complica rápidamente.

+0

Hay podrían ser varias filas con el mismo threadid en la tabla de subprocesamiento, no en la tabla de subprocesos. Esperaba el mismo error que menciona, pero me aparece una "sintaxis incorrecta" más humillante cerca de la palabra clave "GRUPO" " No recomiendo los desencadenantes, ¿qué recomendarías para un caso así? –

+0

Elimina la palabra clave "Fin" al final. (o agrega la palabra "comenzar" después de "como") –

+0

No he visto la edición previamente. la tabla de enhebrado solo tiene 3 campos: threadid, userid, rating ¿Puede explicarnos más sobre la parte del rendimiento? Me decidí por ese concepto completo para p razones de rendimiento ... –

1

Usted puede encontrar los útiles siguiente lectura:

An introduction to Triggers
Wikipedia: DB Triggers

+0

He estado leyendo acerca de los desencadenantes y la forma de SQL Server para hacerlos durante al menos las últimas 3 horas ... Nada ayuda a mi caso particular :( –

+0

Wow, eso es una mierda! Veo si puedo encontrar algunos enlaces mejores. – Kredns

2

D'ohh! Leí totalmente tu pregunta y pensé que estabas preguntando por MySQL. ¡Mea culpa! Dejaré intacta la solución a continuación y la marcaré como wiki de la comunidad. Tal vez sea útil para alguien con un problema similar en MySQL.


MySQL desencadena son ejecutados por fila. También la pseudo-tabla "inserted" es una convención de Microsoft SQL Server.

MySQL utiliza pseudo-tablas NEW y OLD como extensiones al trigger language.

he aquí una solución a su problema:

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 

Del mismo modo que había necesidad de disparadores para UPDATE y 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 
+0

¡Pero estoy usando MS SQL Server! Todo sería más fácil con MySQL, pero no tengo otra opción para este proyecto :( –

+0

D'ohh! Mi error. Ver editar en la parte superior. –

+0

Np, definitivamente será útil para otros :) –

Cuestiones relacionadas