Mysql
 sql >> Base de données >  >> RDS >> Mysql

Meilleur moyen d'empêcher une valeur de devenir négative dans mysql

Je ne peux rien dire sur les performances de la requête, désolé. Mais vous voudrez peut-être envisager des déclencheurs pour éviter que le cas du 'new_balance' ne devienne jamais négatif. (Parce que cela me semble étrange de faire une insertion nulle au cas où le 'new_balance' est inférieur à $amount, mais cela pourrait néanmoins fonctionner :) ).

Voir documentation de MySQL 5.0 pour savoir comment créer un déclencheur.

Fondamentalement, vous placeriez la vérification, si NEW.new_balance est négatif, dans un déclencheur BEFORE. Si oui, alors vous utiliseriez une "STOP ACTION", une erreur délibérée d'exécution, pour abandonner le déclencheur et la requête INSERT. Voir les idées sur la page mentionnée dans les commentaires.

Mise à jour :J'ai un peu bricolé (mon excuse pour installer MySQL à la maison).

Ma version a le problème d'écrire une seconde fois dans la BD pour chaque valeur saisie dans moneylog.

Peut-être que le passage à un proc stocké serait souhaitable. Ou quelqu'un d'autre a une meilleure idée, je ne suis pas très fan de DB :)

CREATE DATABASE triggertest;
CONNECT triggertest;

CREATE TABLE transferlog (
  account SMALLINT UNSIGNED NOT NULL ,
  amount INT NOT NULL,
  new_balance INT NOT NULL
) ENGINE=INNODB;

CREATE TABLE stopaction (
  entry CHAR(20) NOT NULL,
  dummy SMALLINT,
  UNIQUE(`entry`)
);

INSERT INTO stopaction (`entry`) VALUES ('stop');

DELIMITER #
CREATE TRIGGER nonneg_insert BEFORE INSERT ON transferlog
  FOR EACH ROW BEGIN
    INSERT INTO stopaction (`entry`)
      SELECT CASE WHEN NEW.new_balance<0 THEN 'stop'
                  ELSE 'none' END;
    DELETE FROM stopaction WHERE entry!='stop';
  END;
#

CREATE TRIGGER nonneg_update BEFORE UPDATE ON transferlog
  FOR EACH ROW BEGIN
    INSERT INTO stopaction (`entry`)
      SELECT CASE WHEN NEW.new_balance<0 THEN 'stop'
                  ELSE 'none' END;
    DELETE FROM stopaction WHERE entry!='stop';
  END;
#
DELIMITER ;


INSERT INTO transferlog (`account`, `amount`, `new_balance`)  
  VALUES (1, 1000, 1000);
INSERT INTO transferlog (`account`, `amount`, `new_balance`)
  VALUES (1, -1000, 0);
INSERT INTO transferlog (`account`, `amount`, `new_balance`)
  VALUES (1, -1000, -1000);
INSERT INTO transferlog (`account`, `amount`, `new_balance`)
  VALUES (1, 10, 20);
SELECT version();

DROP DATABASE triggertest;

Peut-être que cela vous conviendra, ma sortie pour les INSERT-Lines est :

mysql> INSERT INTO transferlog (`account`, `amount`, `new_balance`)  VALUES (1, 1000, 1000);
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO transferlog (`account`, `amount`, `new_balance`)  VALUES (1, -1000, 0);
Query OK, 1 row affected (0.02 sec)

mysql> INSERT INTO transferlog (`account`, `amount`, `new_balance`)  VALUES (1, -1000, -1000);
ERROR 1062 (23000): Duplicate entry 'stop' for key 1

mysql> INSERT INTO transferlog (`account`, `amount`, `new_balance`)  VALUES (1, 10, 20);
Query OK, 1 row affected (0.02 sec)

mysql> SELECT version();
+---------------------+
| version()           |
+---------------------+
| 5.0.67-community-nt |
+---------------------+
1 row in set (0.00 sec)