2008-10-23 13 views
14

Al limpiar this answer aprendí un poco sobre TRIGGER sy los procedimientos almacenados en MySQL, pero quedé atónito de que, aunque los disparadores BEFORE INSERT y BEFORE UPDATE podían modificar los datos, aparentemente no podían hacer que fallara la inserción/actualización (es decir, la validación) . En este caso particular, pude hacer que funcionara manipulando los datos de tal manera que causara una duplicación de clave primaria, que en este caso particular tenía sentido, pero que no necesariamente tiene sentido en un sentido general.¿Disparadores que hacen que INSERT fallen? ¿Posible?

¿Es posible este tipo de funcionalidad en MySQL? En cualquier otro RDBMS (mi experiencia se limita a MySQL tristemente)? Tal vez una sintaxis de estilo THROW EXCEPTION?

+1

de MySQL 5.5, posiblemente anterior, puede utilizar las señales, [véase mi respuesta aquí] [1] [1]: http://stackoverflow.com/questions/24/throw-error- in-mysql-trigger/7189396 # 7189396 – RuiDC

Respuesta

19

De esta blog post

MySQL Triggers: How do you abort an INSERT, UPDATE or DELETE with a trigger? On EfNet’s #mysql someone asked:

How do I make a trigger abort the operation if my business rule fails?

In MySQL 5.0 and 5.1 you need to resort to some trickery to make a trigger fail and deliver a meaningful error message. The MySQL Stored Procedure FAQ says this about error handling:

SP 11. Do SPs have a “raise” statement to “raise application errors”? Sorry, not at present. The SQL standard SIGNAL and RESIGNAL statements are on the TODO.

Perhaps MySQL 5.2 will include SIGNAL statement which will make this hack stolen straight from MySQL Stored Procedure Programming obsolete. What is the hack? You’re going to force MySQL to attempt to use a column that does not exist. Ugly? Yes. Does it work? Sure.

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; 
+5

De hecho, me reí leyendo este ejemplo ... Es un hack impresionante que se reproduce en el informe de errores de MySQL ... Aquí esperamos SIGNAL pronto. –

+0

Eso es tan increíble. He estado configurando una columna NOT NULL en NULL, pero esta solución arroja un mensaje de error significativo. +1! –

+1

Esto falla para mí cuando la declaración declare está dentro de la condición. Los documentos dicen: [DECLARAR está permitido solo dentro de una sentencia compuesta BEGIN ... END y debe estar en su inicio, antes que cualquier otra declaración.] (Http://dev.mysql.com/doc/refman/5.0/en/ declare.html). –

1

Esto abortará su INSERT elevando una excepción (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!'); 
8

Aquí es la forma en que lo hice. Tenga en cuenta el SET NEW='some error';. MySQL le dirá "Variable 'nueva' no se puede establecer en el valor de 'Error: no se puede eliminar este elemento.' Hay registros en la tabla de ventas con este elemento '"

Puede atrapar esto en su código y a continuación, mostrar el error 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 ; 
1

trabajo doen't en disparadores (SQL dinámico no está permitido en función de almacenado o disparador)

utilizo una solución muy sucio:

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

Cuando test = 1 Mysql lanza la siguiente excepción:

PROCEDIMIENTO administratie.TEST_CANNOT_BE_SET_TO_1 no existe

No es sofisticado, pero rápido y muy útil.

3

Debido a que este artículo se acerca a la parte superior cuando busco el manejo de errores en desencadenadores de MySQL, pensé en compartir algunos conocimientos.

Si hay un error, puede forzar a MySQL a usar un SIGNAL, pero si no lo especifica como una clase como SQLEXCEPTION, entonces no pasará nada, ya que no todos los SQLSTATE se consideran incorrectos, e incluso entonces Tendrías que asegurarte de RESIGNAL si tienes bloques BEGIN/END anidados.

Alternativamente, y probablemente aún más simple, dentro de su activador, declare un manejador de salida y resigne la excepción.

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 

Y si en su cuerpo se produce un error, se lanzará de nuevo a la parte superior y saldrá con un error. Esto también se puede usar en procedimientos almacenados y otras cosas.

Esto funciona con cualquier versión 5.5+.

+0

Votando esto porque es correcto, pero desafortunadamente, no todos tenemos el lujo de trabajar con 5.5+ tampoco. Oh bien. –

Cuestiones relacionadas