Chamar um procedimento armazenado a partir de um gatilho

17

Eu criei um procedimento armazenado no mysql usando a seguinte sintaxe.

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 sua informação, simplifiquei bastante o procedimento armazenado, mas sei que funciona sem problemas.

O que eu gostaria de poder fazer é configurar um gatilho a partir de usergroup_comments que funciona assim.

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;

Mas, por alguma razão, toda vez que eu faço o mysql lança um erro para mim, isso é menos do que útil, afirmando que há um erro de sintaxe na linha 4.

Eu procurei na documentação do mysql e encontrei algumas informações sobre restrições de gatilhos, mas achei bastante complicado.

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

Qualquer idéia será útil.

Mark D
fonte
Portanto, o problema com o procedimento armazenado acima sendo chamado foi o fato de ele ter um hífen em seu nome. Alterar o nome do procedimento armazenado para sp_set_comment_count resolveu o problema.
Mark D

Respostas:

24

Há uma grande razão pela qual você nunca deve chamar procedimentos armazenados a partir de gatilhos.

Os gatilhos são, por natureza, procedimentos armazenados. Suas ações são praticamente difíceis de reverter . Mesmo que todas as tabelas subjacentes sejam o InnoDB, você experimentará um volume proporcional de bloqueios de linha compartilhados e intermitência irritante dos bloqueios de linha exclusivos. Esse seria o caso se os gatilhos estivessem manipulando tabelas com INSERTs e UPDATEs estagnados para executar MVCC de serviço pesado dentro de cada chamada para um gatilho .

Não esqueça que os gatilhos exigem sobrecarga. De fato, de acordo com a Programação de procedimentos armazenados do MySQL , a página 256, sob o cabeçalho "Trigger Overhead", diz o seguinte:

É importante lembrar que, por necessidade, os gatilhos adicionam sobrecarga à instrução DML à qual eles se aplicam. a quantidade real de sobrecarga dependerá da natureza do gatilho, mas --- como todos os gatilhos do MySQL executam FOR EACH ROW --- a sobrecarga pode se acumular rapidamente para instruções que processam um grande número de linhas. Portanto, você deve evitar colocar quaisquer instruções SQL caras ou código de procedimento nos gatilhos.

Uma explicação expandida da sobrecarga do acionador é fornecida nas páginas 529-531. O ponto final dessa seção afirma o seguinte:

A lição aqui é a seguinte: como o código do acionador será executado uma vez para cada linha afetada por uma instrução DML, o acionador pode facilmente se tornar o fator mais significativo no desempenho do DML. O código dentro do corpo do gatilho precisa ser o mais leve possível e - em particular - qualquer instrução SQL no gatilho deve ser suportada por índices sempre que possível.

Expliquei outros aspectos desagradáveis ​​do Triggers em um post anterior.

RESUMO

Eu recomendaria fortemente não chamar nenhum procedimento armazenado de um Trigger , mesmo que o MySQL permita. Você deve verificar as restrições atuais para o MySQL 5.5 .

RolandoMySQLDBA
fonte
Interessante, obrigado pelo aviso. A falta de consultas transacionais em nosso ambiente atenua o problema da transação. No entanto, posso apreciar a ideia de acumular despesas gerais. Acho que vou assistir o banco de dados por um tempo para ver qual é o resultado dessa alteração.
Mark D
Eu não acho que é preciso combinar gatilhos com procedimentos armazenados. No mínimo, é válido iniciar e confirmar uma transação em um procedimento armazenado. O MySQL reclama se você tentar fazer o mesmo em um gatilho. O que é tolo, porque ter um gatilho que precise atualizar transacionalmente uma ou mais tabelas em resposta a alguma mudança é um caso de uso totalmente válido, que deve ser suportado de maneira direta.
Aroth
Então, eu tenho esse gatilho, que é realmente grande. Ele executa vários cálculos na minha tabela, tanto na inserção quanto na atualização. Os gatilhos no Mysql podem realmente se tornar dolorosos quando são complexos. Seria muito mais fácil dividir o gatilho em procedimentos.
Lamar
8

Acontece que esse é o problema que me atormentou por algumas horas, acredite ou não.

Eu posso definir facilmente um procedimento chamado sp_set-comment_count. No entanto, ao chamar o referido procedimento, ele não funciona da mesma maneira.

CHAMADA sp_set-comment_count (só posso assumir que isso ocorre porque o servidor interpreta o - como um sinal de menos).

Desde então, alterei o nome do procedimento armazenado para usar apenas sublinhados e parece ter resolvido tudo.

Mark D
fonte
Tarde para a festa, mas: você criou seu SP usando um identificador entre aspas, que permitia caracteres especiais em seu nome, portanto, você deve referenciá-lo da mesma forma em outros lugares:CALL `sp-set-comment_count`(NEW.`gid`);
mustaccio
5

Se houver algum erro de sintaxe, é mais provável que você tenha esquecido de alterar o delimitador (como fez no procedimento armazenado). Então você precisa

DELIMITER $$
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
   CALL sp_set_count(NEW.`gid`);
END;
$$
a1ex07
fonte
Obrigado, isso realmente me fez pensar no caminho certo. Na verdade, meu sp foi chamado sp-set_comment_count. Quando chamado por um gatilho, parece que o problema era que, quando ligava para o SP a partir do gatilho, continuava lançando o erro.
Mark D
1

Parece que a vírgula depois ACé um erro de sintaxe:

UPDATE usergroups
   SET allCount = AC,
 WHERE ........
user22800
fonte
Ponto válido, mas não a causa real do erro, neste caso, simplesmente aparei alguns conjuntos extras dessa consulta e esqueci de remover o,
Mark D