TRIGGERs qui font échouer les INSERT? Possible?
Question
En nettoyant cette réponse J'ai appris un peu sur les TRIGGER
et les procédures stockées dans MySQL, mais je suis étonné que, alors que BEFORE INSERT
et BEFORE UPDATE
déclenchent pourraient modifier les données, ils ne pourraient apparemment pas causer l'échec de l'insertion / mise à jour (c.-à-d. validation). Dans ce cas particulier, j’ai réussi à obtenir que cela fonctionne en manipulant les données de manière à provoquer une duplication de la clé primaire, ce qui dans ce cas particulier avait un sens, mais n’avait pas nécessairement de sens au sens général.
Ce type de fonctionnalité est-il possible dans MySQL? Dans tout autre SGBDR (mon expérience est malheureusement limitée à MySQL)? Peut-être une syntaxe de style THROW EXCEPTION
?
La solution
À partir de cela un article de blog
Déclencheurs MySQL: Comment annuler une opération INSERT, UPDATE ou DELETE avec une déclencheur? Quelqu'un sur #mysql d’EfNet a demandé:
Comment puis-je faire en sorte qu'un déclencheur annule l'opération en cas d'échec de ma règle métier?
Dans MySQL 5.0 et 5.1, vous devez avoir recours à des ruses pour faire un déclencher l'échec et livrer un sens Message d'erreur. Le MySQL stocké Procédure FAQ dit ceci à propos d'erreur manipulation:
SP 11. Les SP ont-ils une instruction «lever» pour «générer des erreurs d'application»? Désolé, pas pour le moment. Les instructions SIGNAL et RESIGNAL standard SQL figurent sur le TODO.
Peut-être que MySQL 5.2 inclura SIGNAL déclaration qui fera de ce hack volé directement de MySQL Stored Procédure de programmation obsolète. Quoi est le hack? Vous allez forcer MySQL pour essayer d'utiliser une colonne qui n'existe pas. Laid? Oui. Le fait-il travail? Bien sûr.
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;
Autres conseils
Voici comment je l’ai fait. Notez le SET NEW = 'une erreur';
. MySQL vous indiquera que la variable 'new' ne peut pas être définie sur la valeur 'Erreur: Impossible de supprimer cet élément. La table des ventes contient des enregistrements avec cet élément. '& Quot;
Vous pouvez intercepter cela dans votre code, puis afficher l'erreur résultante:)
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 ;
Parce que cet article arrive en tête lorsque je recherche la gestion des erreurs dans les déclencheurs MySQL, j'ai décidé de partager certaines connaissances.
En cas d'erreur, vous pouvez forcer MySQL à utiliser un SIGNAL , mais si vous ne le spécifiez pas en tant que classe sous la forme SQLEXCEPTION, rien ne se passera, car tous les SQLSTATE ne sont pas considérés comme incorrects, et même dans ce cas, vous devez vous assurer que RESIGNAL si vous avez des blocs BEGIN / END imbriqués .
Sinon, et probablement plus simplement encore, dans votre déclencheur, déclarez un gestionnaire d'exit et renvoyez l'exception.
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
Et si dans votre corps se produit une erreur, elle sera renvoyée vers le haut et sortira avec une erreur. Cela peut également être utilisé dans les procédures stockées et ainsi de suite.
Ceci fonctionne avec tout ce que la version 5.5 +.
Ceci annulera votre INSERT en générant une exception (à partir 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$
Utilisation:
call MyRaiseError('Here error message!');
Ne travaillez pas dans les déclencheurs (Le SQL dynamique n'est pas autorisé dans la fonction ou le déclencheur stocké)
J'utilise une solution très sale:
Si NEW.test = 1 alors
CALL TEST_CANNOT_BE_SET_TO_1;
fin si;
When test = 1 Mysql lève l'exception suivante:
PROCEDURE administratie.TEST_CANNOT_BE_SET_TO_1 n'existe pas
Pas sophistiqué mais rapide et utile.
dans MS SQL, vous pourriez le faire fonctionner avec la syntaxe appropriée:
IF UPDATE(column_name)
BEGIN
RAISEERROR
ROLLBACK TRAN
RETURN
END