web-dev-qa-db-fra.com

Erreur MySql: Impossible de mettre à jour la table dans la fonction / le déclencheur enregistré car il est déjà utilisé par une instruction qui a appelé cette fonction / ce déclencheur stocké

J'exécute une requête MySQL. Mais quand une nouvelle ligne est ajoutée à partir d'une entrée de formulaire, j'obtiens cette erreur:

Error: Can't update table 'brandnames' in stored function/trigger because it is 
already used by statement which invoked this stored function/trigger.

À partir du code:

CREATE TRIGGER `capital` AFTER INSERT ON `brandnames`
FOR EACH
ROW UPDATE brandnames
SET bname = CONCAT( UCASE( LEFT( bname, 1 ) ) , LCASE( SUBSTRING( bname, 2 ) ) )

Que signifie cette erreur?

45
Mitesh Mynee

Vous ne pouvez pas modifier une table lorsque le déclencheur INSERT est activé. L'INSERT peut faire un certain verrouillage qui pourrait entraîner un blocage. De même, la mise à jour de la table à partir d'un déclencheur provoquerait le déclenchement du même déclencheur dans une boucle infinie récursive. C'est pour ces deux raisons que MySQL vous en empêche.

Toutefois, en fonction de vos objectifs, vous pouvez accéder aux nouvelles valeurs en utilisant NEW.fieldname ou même aux anciennes valeurs (si vous effectuez une MISE À JOUR) avec OLD.

Si vous aviez une ligne nommée full_brand_name et vous vouliez utiliser les deux premières lettres comme nom abrégé dans le champ small_name vous pouvez utiliser:

CREATE TRIGGER `capital` BEFORE INSERT ON `brandnames`
FOR EACH ROW BEGIN
  SET NEW.short_name = CONCAT(UCASE(LEFT(NEW.full_name,1)) , LCASE(SUBSTRING(NEW.full_name,2)))
END
73
Steve

La syntaxe correcte est la suivante:

FOR EACH ROW SET NEW.bname = CONCAT( UCASE( LEFT( NEW.bname, 1 ) )
                                   , LCASE( SUBSTRING( NEW.bname, 2 ) ) )
7
rsanchez

Un déclencheur "BEFORE-INSERT" est le seul moyen de réaliser des mises à jour de même table sur une insertion et n'est possible qu'à partir de MySQL 5.5+. Cependant, la valeur d'un champ auto-incrémenté n'est disponible que pour un déclencheur "AFTER-INSERT" - la valeur par défaut est 0 dans le cas BEFORE. Par conséquent, l'exemple de code suivant qui définirait une valeur de clé de substitution calculée précédemment sur la base de la valeur d'auto-incrémentation id sera compilé, mais ne fonctionnera pas car NEW.id sera toujours égal à 0:

create table products(id int not null auto_increment, surrogatekey varchar(10), description text);
create trigger trgProductSurrogatekey before insert on product
for each row set NEW.surrogatekey = 
  (select surrogatekey from surrogatekeys where id = NEW.id);
5
gerrit_hoekstra

J'ai le même problème et corrige en ajoutant "nouveau". avant que le champ est mis à jour. Et je poste un déclencheur complet ici pour que quelqu'un veuille écrire un déclencheur

DELIMITER $$

USE `nc`$$

CREATE
    TRIGGER `nhachung_province_count_update` BEFORE UPDATE ON `nhachung` 
    FOR EACH ROW BEGIN

    DECLARE slug_province VARCHAR(128);
    DECLARE slug_district VARCHAR(128);

    IF old.status!=new.status THEN  /* neu doi status */
        IF new.status="Y" THEN
            UPDATE province SET `count`=`count`+1 WHERE id = new.district_id;
        ELSE 
            UPDATE province SET `count`=`count`-1 WHERE id = new.district_id;
        END IF;
    ELSEIF old.province_id!=new.province_id THEN /* neu doi province_id + district_id */

        UPDATE province SET `count`=`count`+1 WHERE id = new.province_id; /* province_id */
        UPDATE province SET `count`=`count`-1 WHERE id = old.province_id;
        UPDATE province SET `count`=`count`+1 WHERE id = new.district_id; /* district_id */
        UPDATE province SET `count`=`count`-1 WHERE id = old.district_id;

        SET slug_province = ( SELECT slug FROM province WHERE id= new.province_id LIMIT 0,1 );
        SET slug_district = ( SELECT slug FROM province WHERE id= new.district_id LIMIT 0,1 );
        SET new.prov_dist_url=CONCAT(slug_province, "/", slug_district);

    ELSEIF old.district_id!=new.district_id THEN 

        UPDATE province SET `count`=`count`+1 WHERE id = new.district_id;
        UPDATE province SET `count`=`count`-1 WHERE id = old.district_id;

        SET slug_province = ( SELECT slug FROM province WHERE id= new.province_id LIMIT 0,1 );
        SET slug_district = ( SELECT slug FROM province WHERE id= new.district_id LIMIT 0,1 );
        SET new.prov_dist_url=CONCAT(slug_province, "/", slug_district);

    END IF;


    END;
$$

DELIMITER ;

J'espère que cela aidera quelqu'un

2
Bảo Nam

@gerrit_hoekstra a écrit: "Cependant, la valeur d'un champ auto-incrémenté n'est disponible que pour un déclencheur" AFTER-INSERT "- la valeur par défaut est 0 dans le cas BEFORE."

C'est correct, mais vous pouvez sélectionner assez facilement la valeur du champ à incrémentation automatique qui sera insérée par l'insert suivant. Ceci est un exemple qui fonctionne:

CREATE DEFINER = CURRENT_USER TRIGGER `lgffin`.`variable_BEFORE_INSERT` BEFORE INSERT 
ON `variable` FOR EACH ROW
BEGIN
    SET NEW.prefixed_id = CONCAT(NEW.fixed_variable, (SELECT `AUTO_INCREMENT`
                                                FROM  INFORMATION_SCHEMA.TABLES
                                                WHERE TABLE_SCHEMA = 'lgffin'
                                                AND   TABLE_NAME   = 'variable'));      
END
0
rf1234