Pergunta

Na limpeza esta resposta Eu aprendi um pouco sobre TRIGGERs e procedimentos armazenados em MySQL, mas ficou surpreso que, enquanto BEFORE INSERT e BEFORE UPDATE Os gatilhos podem modificar os dados, aparentemente não podiam fazer com que a inserção/atualização falhe (por exemplo, validação). Nesse caso em particular, consegui fazer isso funcionar manipulando os dados de forma a causar uma duplicata de chave primária, que nesse caso em particular fazia sentido, mas não necessariamente faz sentido em um sentido geral.

Esse tipo de funcionalidade é possível no MySQL? Em qualquer outro RDBMS (minha experiência é limitada a MySQL tristemente)? Talvez a THROW EXCEPTION sintaxe de estilo?

Foi útil?

Solução

A partir disso Postagem do blog

MySQL gatilhos: Como você aborta uma inserção, atualização ou exclua com um gatilho? No #MySQL da EFNET, alguém perguntou:

Como faço para fazer um gatilho abortar a operação se minha regra de negócios falhar?

No MySQL 5.0 e 5.1, você precisa recorrer a alguns truques para fazer um gatilho falhar e entregar uma mensagem de erro significativa. O procedimento armazenado MySQL FAQ diz isso sobre o tratamento de erros:

SP 11. O SPS tem uma declaração de "aumento" para "aumentar os erros de inscrição"? Desculpe, não atualmente. O sinal padrão SQL e as declarações resignais estão no TODO.

Talvez o MySQL 5.2 inclua a instrução SINAL, que tornará obsoleto esse hackeado do MySQL Programação de procedimentos armazenados. O que é o hack? Você vai forçar o MySQL a tentar usar uma coluna que não existe. Feio? Sim. Funciona? Claro.

CREATE TRIGGER mytabletriggerexample
BEFORE INSERT
FOR EACH ROW BEGIN
IF(NEW.important_value) < (fancy * dancy * calculation) THEN
    DECLARE dummy INT;

    SELECT Your meaningful error message goes here INTO dummy 
        FROM mytable
      WHERE mytable.id=new.id
END IF; END;

Outras dicas

Aqui está a maneira como eu fiz isso. Note o SET NEW='some error';. O MySQL informará que "variável 'novo' não pode ser definida como o valor de 'Erro: não é possível excluir este item. Existem registros na tabela de vendas com este item.'"

Você pode prender isso no seu código e depois mostrar o erro resultante :)

DELIMITER $$
DROP TRIGGER IF EXISTS before_tblinventoryexceptionreasons_delete $$
CREATE TRIGGER before_tblinventoryexceptionreasons_delete
BEFORE DELETE ON tblinventoryexceptionreasons
FOR EACH ROW BEGIN
  IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblinventoryexceptionreasons = old.idtblinventoryexceptionreasons) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the inventory exception reasons table with this item.';
  END IF;
END$$
DELIMITER ;

DELIMITER $$
DROP TRIGGER IF EXISTS before_storesalesconfig_delete $$
CREATE TRIGGER before_storesalesconfig_delete
BEFORE DELETE ON tblstoresalesconfig
FOR EACH ROW BEGIN
  IF (SELECT COUNT(*) FROM tblstoresales WHERE tblstoresales.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the sales table with this item.';
  END IF;
  IF (SELECT COUNT(*) FROM tblinventory WHERE tblinventory.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the inventory table with this item.';
  END IF;
  IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the inventory exceptions table with this item.';
  END IF;
  IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.';
  END IF;
END$$
DELIMITER ;

DELIMITER $$
DROP TRIGGER IF EXISTS before_tblinvoice_delete $$
CREATE TRIGGER before_tblinvoice_delete
BEFORE DELETE ON tblinvoice
FOR EACH ROW BEGIN
  IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblinvoice = old.idtblinvoice) > 0
  THEN
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.';
  END IF;
END$$
DELIMITER ;

Como este artigo aparece no topo quando procuro o manuseio de erros nos gatilhos do MySQL, pensei em compartilhar algum conhecimento.

Se houver um erro, você pode forçar o MySQL a usar um SINAL, mas se você não especificar isso como uma classe como sqlexception, nada acontecerá, pois nem todos os sqlstates são considerados ruins, e mesmo assim você teria que se certificar de Resignal Se você tiver algum bloco de início/final aninhado.

Como alternativa, e provavelmente mais simples ainda, dentro do seu gatilho, declare um manipulador de saída e renuncia a exceção.

CREATE TRIGGER `my_table_AINS` AFTER INSERT ON `my_table` FOR EACH ROW
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
        RESIGNAL;
    DECLARE EXIT HANDLER FOR SQLWARNING
        RESIGNAL;
    DECLARE EXIT HANDLER FOR NOT FOUND
        RESIGNAL; 
    -- Do the work of the trigger.
END

E se no seu corpo ocorrer um erro, ele será jogado de volta à parte superior e sairá com um erro. Isso também pode ser usado em procedimentos armazenados e outros enfeites.

Isso funciona com qualquer coisa versão 5.5+.

Isso abortará sua inserção levantando uma exceção (de http://www.experts-exchange.com/database/mysql/q_23788965.html)

DROP PROCEDURE IF EXISTS `MyRaiseError`$$

CREATE PROCEDURE `MyRaiseError`(msg VARCHAR(62))
BEGIN
DECLARE Tmsg VARCHAR(80);
SET Tmsg = msg;
IF (CHAR_LENGTH(TRIM(Tmsg)) = 0 OR Tmsg IS NULL) THEN
SET Tmsg = 'ERROR GENERADO';
END IF;
SET Tmsg = CONCAT('@@MyError', Tmsg, '@@MyError');
SET @MyError = CONCAT('INSERT INTO', Tmsg);
PREPARE stmt FROM @MyError;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$

Uso:

call MyRaiseError('Here error message!');

Não funciona em gatilhos (o SQL dinâmico não é permitido na função ou gatilho armazenado)

Eu uso uma solução muito suja:

If NEW.test=1 then CALL TEST_CANNOT_BE_SET_TO_1; end if;

Quando teste = 1 MySQL lança a seguinte exceção:

Procedimento Administratie.test_cannot_be_set_to_1 não existe

Não sofisticado, mas rápido e útil.

No MS SQL, você pode fazê -lo funcionar usando sintaxe adequada:

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

http://msdn.microsoft.com/en-us/magazine/cc164047.aspx

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top