Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Melhor maneira de evitar que um valor fique negativo no mysql


Não posso dizer nada sobre o desempenho da consulta, desculpe. Mas você pode querer considerar gatilhos para evitar que o caso do 'new_balance' se torne negativo. (Porque me parece estranho fazer uma inserção nula no caso de 'new_balance' ser menor que $amount, mas pode funcionar mesmo assim :) ).

Consulte a documentação do MySQL 5.0 para obter detalhes sobre como criar um gatilho.

Basicamente você colocaria o cheque, se NEW.new_balance for negativo em um BEFORE-trigger. Se sim, então você usaria um "STOP ACTION", um erro deliberado na execução, para abortar o gatilho e a consulta INSERT. Veja ideias na página mencionada nos comentários.

Atualização:Acertei um pouco (minha desculpa para instalar o MySQL em casa).

Minha versão tem o problema de escrever uma segunda vez no DB para cada valor inserido no moneylog.

Talvez mudar para um proc armazenado seja aconselhável. Ou alguém tem uma ideia melhor, eu não gosto muito 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;

Talvez seja adequado para você, minha saída para as linhas INSERT é:
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)