Mysql
 sql >> Database >  >> RDS >> Mysql

Il modo migliore per evitare che un valore diventi negativo in MySQL

Non posso dire nulla sull'esecuzione della query, mi dispiace. Ma potresti prendere in considerazione i trigger per evitare che il caso del "new_balance" diventi negativo. (Perché mi sembra strano fare un inserimento nullo nel caso in cui 'new_balance' sia inferiore a $amount, ma potrebbe comunque funzionare :)).

Vedi documentazione di MySQL 5.0 per i dettagli su come creare un trigger.

Fondamentalmente dovresti mettere il segno di spunta, se NEW.new_balance è negativo in un PRIMA-trigger. In caso affermativo, utilizzeresti un "STOP ACTION", un errore deliberato nell'esecuzione, per interrompere il trigger e la query INSERT. Vedi le idee nella pagina menzionata nei commenti.

Aggiornamento:ho armeggiato un po' (la mia scusa per installare MySQL a casa).

La mia versione ha il problema di scrivere una seconda volta nel DB per ogni valore inserito in moneylog.

Forse sarebbe consigliabile passare a un processo memorizzato. O qualcun altro ha un'idea migliore, non sono molto interessato a 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;

Forse ti si addice, il mio output per le linee 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)