web-dev-qa-db-fra.com

Déclencheur MySQL - Stocker un SELECT dans une variable

J'ai un déclencheur dans lequel je veux avoir une variable qui contient un INT que je reçois d'une SELECT, je peux donc l'utiliser dans deux instructions IF au lieu d'appeler deux fois la SELECT Comment déclarez-vous/utilisez-vous les variables dans les déclencheurs MySQL?

48
Ice

Vous pouvez déclarer des variables locales dans les déclencheurs MySQL, avec la syntaxe DECLARE.

Voici un exemple:

DROP TABLE IF EXISTS foo;
CREATE TABLE FOO (
  i SERIAL PRIMARY KEY
);

DELIMITER //
DROP TRIGGER IF EXISTS bar //

CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
  DECLARE x INT;
  SET x = NEW.i;
  SET @a = x; -- set user variable outside trigger
END//

DELIMITER ;

SET @a = 0;

SELECT @a; -- returns 0

INSERT INTO foo () VALUES ();

SELECT @a; -- returns 1, the value it got during the trigger

Lorsque vous affectez une valeur à une variable, vous devez vous assurer que la requête ne renvoie qu'une seule valeur, et non un ensemble de lignes ou un ensemble de colonnes. Par exemple, si votre requête renvoie une valeur unique dans la pratique, c'est correct, mais dès qu'elle renvoie plusieurs lignes, vous obtenez "ERROR 1242: Subquery returns more than 1 row". 

Vous pouvez utiliser LIMIT ou MAX() pour vous assurer que la variable locale est définie sur une valeur unique.

CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
  DECLARE x INT;
  SET x = (SELECT age FROM users WHERE name = 'Bill'); 
  -- ERROR 1242 if more than one row with 'Bill'
END//

CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
  DECLARE x INT;
  SET x = (SELECT MAX(age) FROM users WHERE name = 'Bill');
  -- OK even when more than one row with 'Bill'
END//
45
Bill Karwin
`CREATE TRIGGER `category_before_ins_tr` BEFORE INSERT ON `category`
  FOR EACH ROW
BEGIN
    **SET @tableId= (SELECT id FROM dummy LIMIT 1);**

END;`;
6
IgorS
CREATE TRIGGER clearcamcdr AFTER INSERT ON `asteriskcdrdb`.`cdr` 
FOR EACH ROW
BEGIN
  SET @INC = (SELECT sip_inc FROM trunks LIMIT 1);
  IF NEW.billsec >1 AND NEW.channel LIKE @INC 
    AND NEW.dstchannel NOT LIKE "" 
  THEN
    insert into `asteriskcdrdb`.`filtre` (id_appel,date_appel,source,destinataire,duree,sens,commentaire,suivi) 
      values (NEW.id,NEW.calldate,NEW.src,NEW.dstchannel,NEW.billsec,"entrant","",""); 
  END IF;
END$$

Ne pas essayer ceci @ la maison

5
autobulkvm

Ou vous pouvez simplement inclure l’instruction SELECT dans le SQL qui appelle le déclencheur, afin qu’elle soit transmise comme l’une des colonnes de la ou des lignes du déclencheur. Tant que vous êtes certain qu'il ne renverra infailliblement qu'une seule ligne (donc une valeur). (Et, bien sûr, il ne doit pas renvoyer une valeur qui interagit avec la logique dans le déclencheur, mais c'est vrai dans tous les cas.)

1
dkretz

Je poste cette solution parce que j'ai eu du mal à trouver ce dont j'avais besoin. Ce message m’a suffisamment rapproché (+1 pour ce merci), et voici la solution finale pour réorganiser les données de la colonne avant insertion si les données correspondent à un test .

Remarque: ceci provient d'un projet legacy dont j'ai hérité où:

  1. La clé unique est composée de rridprefix + rrid
  2. Avant de prendre ma place, aucune contrainte n'empêchait la duplication de clés uniques
  3. Nous devions combiner deux tables (une pleine de doublons) dans la table principale qui a maintenant la contrainte sur la clé composite (ainsi la fusion échoue car la table gagnante n'autorisera pas les doublons de la table non nettoyée)
  4. on duplicate key est moins qu'idéal car les colonnes sont trop nombreuses et peuvent changer

Quoi qu'il en soit, voici le déclencheur qui place les clés en double dans une colonne héritée tout en nous permettant de stocker les données héritées et incorrectes (et non de déclencher la clé composite de tables gagnantes, unique) .

BEGIN
  -- prevent duplicate composite keys when merging in archive to main
  SET @EXIST_COMPOSITE_KEY = (SELECT count(*) FROM patientrecords where rridprefix = NEW.rridprefix and rrid = NEW.rrid);

  -- if the composite key to be introduced during merge exists, rearrange the data for insert
  IF @EXIST_COMPOSITE_KEY > 0
  THEN

    -- set the incoming column data this way (if composite key exists)

    -- the legacy duplicate rrid field will help us keep the bad data
    SET NEW.legacyduperrid = NEW.rrid;

    -- allow the following block to set the new rrid appropriately
    SET NEW.rrid = null;

  END IF;

  -- legacy code tried set the rrid (race condition), now the db does it
  SET NEW.rrid = (
    SELECT if(NEW.rrid is null and NEW.legacyduperrid is null, IFNULL(MAX(rrid), 0) + 1, NEW.rrid)
    FROM patientrecords
    WHERE rridprefix  = NEW.rridprefix
  );
END
0
WEBjuju