TRIGGERs que causan fallos en INSERTs? ¿Posible?
Pregunta
Al limpiar esta respuesta Aprendí un poco sobre los TRIGGER
y los procedimientos almacenados en MySQL, pero me sorprendió, mientras que ANTES DE INSERTAR
y ANTES DE ACTUALIZAR
se activan podrían modificar los datos, aparentemente no pudieron causar que la inserción / actualización fallara (es decir, validación). En este caso particular, pude hacer que esto funcionara manipulando los datos de tal manera que causara un duplicado de la clave principal, lo que en este caso en particular tenía sentido, pero no necesariamente tiene sentido en un sentido general. p>
¿Es este tipo de funcionalidad posible en MySQL? En cualquier otro RDBMS (mi experiencia se limita a MySQL, por desgracia)? Tal vez una sintaxis de estilo de EXKE EXCEPTION
?
Solución
De esto publicación de blog
MySQL Triggers: ¿Cómo abortar un INSERTAR, ACTUALIZAR o BORRAR con un ¿desencadenar? En la #mysql de EfNet alguien preguntó:
¿Cómo hago que un desencadenante aborte la operación si falla mi regla de negocios?
En MySQL 5.0 y 5.1 necesitas recurrir a algún engaño para hacer una desencadenar el fallo y entregar un significativo mensaje de error. El MySQL almacenado Preguntas frecuentes sobre el procedimiento dice esto sobre el error manejo:
SP 11. ¿Los SP tienen una declaración de "aumento" para "generar errores de aplicación"? Lo siento, no en la actualidad. Las instrucciones SIGNAL y RESIGNAL estándar de SQL se encuentran en TODO.
Tal vez MySQL 5.2 incluya SIGNAL declaración que hará este hack Robado directamente de MySQL Stored Procedimiento de programación obsoleto. Qué es el hack? Vas a forzar MySQL para intentar usar una columna que no existe. ¿Feo? Sí. Lo hace ¿trabajo? 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;
Otros consejos
Así es como lo hice. Tenga en cuenta que SET NEW = 'some error';
. MySQL le dirá que " Variable 'nuevo' no se puede establecer en el valor de 'Error: No se puede eliminar este elemento. Hay registros en la tabla de ventas con este artículo. '& Quot;
Puede capturar esto en su código y luego 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 ;
Debido a que este artículo aparece en la parte superior cuando busco el manejo de errores en los activadores de MySQL, pensé que compartiría 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 ocurrirá nada, ya que no todos los SQLSTATE se consideran incorrectos, e incluso entonces debería asegurarse de RESIGNAL si tiene bloques BEGIN / END anidados .
Como alternativa, y probablemente aún más simple, dentro de su activador, declare un controlador de salida y renuncie a 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 lo devolverá a la parte superior y saldrá con un error. Esto también se puede utilizar en procedimientos almacenados y otras cosas.
Esto funciona con cualquier versión 5.5+.
Esto abortará su INSERT al generar una excepción 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!');
No funciona en los desencadenantes (SQL dinámico no está permitido en función almacenada o activador)
Uso una solución muy sucia:
Si NEW.test = 1 entonces
CALL TEST_CANNOT_BE_SET_TO_1;
terminara si;
Cuando test = 1 Mysql lanza la siguiente excepción:
la administración de PROCEDIMIENTOS.TEST_CANNOT_BE_SET_TO_1 no existe
No es sofisticado pero es rápido y útil.
en MS SQL puede hacer que funcione con la sintaxis adecuada:
IF UPDATE(column_name)
BEGIN
RAISEERROR
ROLLBACK TRAN
RETURN
END