sql >> Databasteknik >  >> RDS >> Mysql

Bästa sättet att förhindra att ett värde blir negativt i mysql

Jag kan inte säga något om resultatet av frågan, ledsen. Men du kanske vill överväga triggers för att förhindra att "new_balance" någonsin blir negativ. (Eftersom det förefaller mig konstigt att göra en null-insättning i fall 'new_balance' är lägre än $amount, men det kanske fungerar ändå :) ).

Se dokumentation av MySQL 5.0 för detaljer om hur man skapar en utlösare.

I grund och botten skulle du sätta kontrollen om NEW.new_balance är negativ i en BEFORE-trigger. Om ja, skulle du använda en "STOPP ACTION", ett avsiktligt fel i exekveringen, för att avbryta triggern och INSERT-frågan. Se idéer på den nämnda sidan i kommentarerna.

Uppdatering:Tjatade lite (min ursäkt för att installera MySQL hemma).

Min version har problemet med att skriva en andra gång till DB för varje värde som skrivs in i moneylog.

Kanske skulle det vara tillrådligt att byta till en lagrad proc. Eller så har någon annan en bättre idé, jag är inte så mycket för 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;

Kanske kommer det att passa dig, min produktion för INSERT-Lines är:

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)


  1. JavaScript Bekräfta före radering med PHP/MYSQL

  2. PostgreSQL-baserad applikationsprestanda:latens och dolda förseningar

  3. PostgreSQL-uppdateringar med flera rader i Node.js

  4. PostgreSQL bästa inlärnings- och utbildningsresurser