Llamar a un procedimiento almacenado desde un disparador

17

He creado un procedimiento almacenado en mysql usando la siguiente sintaxis.

DROP PROCEDURE IF EXISTS `sp-set_comment_count`;

DELIMITER $$

CREATE PROCEDURE `sp_set-comment_count` (IN _id INT)
BEGIN
   -- AC   - AllCount
   DECLARE AC INT DEFAULT 0;

   SELECT COUNT(*) AS ac
     INTO AC
     FROM usergroups AS ug
LEFT JOIN usergroup_comments AS ugm ON ugm.`gid` = ug.`id`
LEFT JOIN mediagallery AS dm ON ugm.mid = dm.`id`
    WHERE dm.`status` NOT IN (200, 201, 202, 203, 204, 205)
      AND ug.`id` = _id;

   UPDATE usergroups
      SET allCount = AC,
    WHERE usergroups.`id` = _id;

END $$
DELIMITER ;

Para su información, he simplificado enormemente el procedimiento almacenado, pero sé que funciona sin ningún problema.

Lo que me gustaría poder hacer es configurar un activador desde usergroup_comments que funcione de esta manera.

DROP TRIGGER IF EXISTS `usergroups_comments_insert` 

CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
    FOR EACH ROW
    BEGIN
       CALL sp-set-comment_count(NEW.`gid`);
    END;

Pero por alguna razón, cada vez que hago mysql me arroja un error que no es útil, indicando que hay un error de sintaxis en la línea 4.

Revisé la documentación de mysql y encontré información sobre las restricciones de los desencadenantes, pero la encontré bastante complicada.

http://dev.mysql.com/doc/refman/5.1/en/stored-program-restrictions.html

Cualquier idea sería útil.

Mark D
fuente
Resulta que el problema con el procedimiento almacenado anterior que se llamó fue el hecho de que tenía un guión en su nombre. Cambiar el nombre del procedimiento almacenado a sp_set_comment_count resolvió el problema.
Mark D

Respuestas:

24

Hay una gran razón por la cual nunca debe llamar a los procedimientos almacenados desde dentro de los disparadores.

Los disparadores son, por naturaleza, procedimientos almacenados. Sus acciones son prácticamente difíciles de revertir . Incluso si todas las tablas subyacentes son InnoDB, experimentará un volumen proporcional de bloqueos de fila compartidos e intermitencia molesta de bloqueos de fila exclusivos. Tal sería el caso si los activadores estuvieran manipulando tablas con INSERT y ACTUALIZACIONES estancadas para realizar MVCC de servicio pesado dentro de cada llamada a un activador .

No olvide que los disparadores requieren gastos generales. De hecho, de acuerdo con la programación de procedimientos almacenados de MySQL , la página 256 debajo del encabezado "Trigger Overhead" dice lo siguiente:

Es importante recordar que, por necesidad, los desencadenantes agregan una sobrecarga a la declaración DML a la que se aplican. la cantidad real de sobrecarga dependerá de la naturaleza del activador, pero --- como todos los activadores de MySQL se ejecutan PARA CADA FILA --- la sobrecarga puede acumularse rápidamente para las declaraciones que procesan grandes cantidades de filas. Por lo tanto, debe evitar colocar declaraciones SQL costosas o código de procedimiento en los activadores.

En las páginas 529-531 se ofrece una explicación ampliada de la sobrecarga del activador. El punto final de esa sección establece lo siguiente:

La lección aquí es esta: dado que el código de activación se ejecutará una vez por cada fila afectada por una instrucción DML, el activador puede convertirse fácilmente en el factor más significativo en el rendimiento de DML. El código dentro del cuerpo del desencadenador debe ser lo más ligero posible y, en particular, cualquier declaración SQL en el desencadenador debe estar respaldada por índices siempre que sea posible.

Expliqué otros aspectos desagradables de Triggers en una publicación anterior.

RESUMEN

Me gustaría recomendar encarecidamente no llamar a los procedimientos almacenados de un gatillo , aunque MySQL permite. Debería consultar las restricciones actuales para MySQL 5.5 .

RolandoMySQLDBA
fuente
Interesante, gracias por el aviso. La falta de consultas transaccionales en nuestro entorno mitiga el problema de la transacción. Sin embargo, puedo apreciar la idea de acumular gastos generales. Supongo que miraré la base de datos por un tiempo para ver cuál es el resultado de este cambio.
Mark D el
No creo que sea preciso combinar desencadenantes con procedimientos almacenados. Como mínimo, es válido iniciar y confirmar una transacción en un procedimiento almacenado. MySQL se queja si intenta hacer lo mismo en un disparador. Lo cual es una tontería, porque tener un desencadenante que necesita actualizar transaccionalmente una o más tablas en respuesta a algún cambio es un caso de uso completamente válido que debe ser soportado de manera directa.
Aroth
Entonces tengo este disparador, eso es realmente grande. Realiza varios cálculos en mi tabla, tanto al insertar como al actualizar. Los disparadores en Mysql realmente pueden volverse dolorosos cuando son complejos. Sería mucho más fácil dividir el gatillo en procedimientos.
Lamar
8

Resulta que este es el problema que me atormentó durante unas horas, lo creas o no.

Puedo definir fácilmente un procedimiento llamado sp_set-comment_count. Sin embargo, al llamar a dicho procedimiento, no funciona de la misma manera.

LLAME a sp_set-comment_count (solo puedo suponer que esto se debe a que el servidor interpreta el - como un signo menos).

Desde entonces he cambiado el nombre del procedimiento almacenado para usar solo guiones bajos y parece haber resuelto todo.

Mark D
fuente
Tarde para la fiesta, pero: ha creado su SP utilizando un identificador entre comillas, que permite caracteres especiales en su nombre, por lo que debe hacer referencia de manera similar en otro lugar:CALL `sp-set-comment_count`(NEW.`gid`);
mustaccio
5

Si dice acerca del error de sintaxis, lo más probable es que haya olvidado cambiar el delimitador (como lo hizo para el procedimiento almacenado). Así que tú necesitas

DELIMITER $$
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
   CALL sp_set_count(NEW.`gid`);
END;
$$
a1ex07
fuente
Gracias, esto realmente me hizo pensar en el camino correcto. De hecho, mi sp se llamaba sp-set_comment_count. Cuando lo llama un disparador, parece que el problema era que cuando se llamaba al SP desde el disparador, se seguía arrojando el error.
Mark D el
1

Parece que la coma después ACes un error de sintaxis:

UPDATE usergroups
   SET allCount = AC,
 WHERE ........
usuario22800
fuente
Punto válido, pero no la causa real del error en este caso, simplemente recorté algunos conjuntos adicionales de esa consulta y olvidé eliminar el,
Mark D