Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Der beste Weg, um zu verhindern, dass ein Wert in MySQL negativ wird

Zur Performance der Abfrage kann ich nichts sagen, sorry. Aber vielleicht möchten Sie Trigger in Betracht ziehen, um zu verhindern, dass der Fall „new_balance“ jemals negativ wird. (Weil es mir seltsam vorkommt, eine Null-Einfügung vorzunehmen, falls 'new_balance' niedriger als $amount ist, aber es könnte trotzdem funktionieren :) ).

Siehe Dokumentation von MySQL 5.0 für Details zum Erstellen eines Triggers.

Grundsätzlich würden Sie die Prüfung, ob NEW.new_balance negativ ist, in einen BEFORE-Trigger stecken. Wenn ja, dann würden Sie eine "STOP ACTION", einen absichtlichen Fehler in der Ausführung, verwenden, um den Trigger und die INSERT-Abfrage abzubrechen. Siehe Ideen auf der genannten Seite in den Kommentaren.

Update:Ein bisschen herumgebastelt (meine Entschuldigung für die Installation von MySQL zu Hause).

Meine Version hat das Problem, dass sie für jeden in moneylog eingegebenen Wert ein zweites Mal in die DB schreibt.

Vielleicht wäre es ratsam, auf eine gespeicherte Prozedur umzusteigen. Oder jemand anderes hat eine bessere Idee, ich bin nicht so sehr 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;

Vielleicht passt es dir, meine Ausgabe für die INSERT-Zeilen ist:

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)