Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
防止mysql中的值变为负值的最佳方法_Sql_Optimization_Mysql - Fatal编程技术网

防止mysql中的值变为负值的最佳方法

防止mysql中的值变为负值的最佳方法,sql,optimization,mysql,Sql,Optimization,Mysql,我们有一个表,通过在该表中记录交易来维护账户余额。i、 e.最近一行是账户余额 在记录提款时,我们希望确保余额永远不会为负。我们提出的解决方案类似于: INSERT INTO `txns` (`account_id`, `prev_balance`, `txn_type`, `new_balance`, `amount`, `description`) SELECT t.account_id, t.new_balance, $txn_type, t.new_balance - $am

我们有一个表,通过在该表中记录交易来维护账户余额。i、 e.最近一行是账户余额

在记录提款时,我们希望确保余额永远不会为负。我们提出的解决方案类似于:

INSERT INTO `txns`
  (`account_id`, `prev_balance`, `txn_type`, `new_balance`,  `amount`, `description`)
SELECT 
  t.account_id, t.new_balance, $txn_type, t.new_balance - $amount, $amount, $description
FROM`txns` t
WHERE t.account_id = '$account'
  AND (select new_balance 
        FROM txns 
        WHERE account_id = '$account' 
        ORDER BY txn_id desc limit 1) >= $amount
ORDER BY txn_id desc LIMIT 1;"
但是我们有点担心ANDed子查询的性能(我们在以前的项目中遇到过子查询性能问题)。这里的开发人员都不是sql专家。存款没有附加条款


这都是MySQL 5.0上的内容,对不起,我不能对查询的性能说任何话。但您可能需要考虑触发器以防止“NexIoalPalm”的情况变为负值。(因为在“新余额”低于$amount的情况下执行空插入操作让我感到奇怪,但它可能仍然有效:)

有关如何创建触发器的详细信息,请参见

基本上,如果NEW.NEW_balanceí为负数,您会在触发前将支票放入。如果是,那么您将使用“停止操作”,即执行过程中的故意错误,来中止触发器并插入查询。请参见评论中提到的页面上的想法

更新:修改了一下(我在家安装MySQL的借口)。 我的版本存在的问题是,对于输入moneylog的每个值,都会向DB写入第二次。 也许切换到存储过程是可取的。或者其他人有更好的主意,我对DB不太感兴趣:)

你为什么不这样做呢:

INSERT INTO `txns`
  (`account_id`, `prev_balance`, `txn_type`, `new_balance`,  `amount`, `description`)
SELECT *
FROM (
 SELECT
  t.account_id, t.new_balance, $txn_type, t.new_balance - $amount, $amount, $description
 FROM `txns` t
 WHERE t.account_id = '$account'
 ORDER BY txn_id desc
 LIMIT 1
)
WHERE new_balance - $amount > 0

我必须同意触发的想法。如果无论如何输入数据,都必须遵循这一会计规则,则需要将其置于触发器中


如果这仅适用于此特定情况,则在SQL代码中执行此操作。我不知道mySQL,但在SQL server中,我会将检查放在if语句中,如果满足if条件,则会使事务失败。这里的关键不是忽略数据,而是主动使事务失败,否则当数据不符合要输入的标准时,用户认为数据已经输入。我决不会为没有封装在事务中的金融系统编写任何cdode,如果不符合业务规则,它将回滚整个事务并向用户发送错误。业务规则对于金融应用程序来说非常关键(并且通常应该在触发器中,这样无论数据如何放入系统,它们都不会丢失)如果所有步骤都不成功,并且您不在事务中,并且在出现问题时回滚,那么数据完整性可能会成为一个真正的问题。

这是完全正确的。触发器非常适合这种情况,因此只需让触发器使语句执行失败并捕获错误。如果余额为负数,则附加子句根本不会发生插入,因此它不是空插入(我查看受影响的行数,如果没有插入行,则引发异常)至于触发器,我已经对此进行了一些研究,但棘手的部分似乎是导致了一个人工错误来中止insert.MySQL不执行检查约束(换句话说,我只会使用它)。我目前的解决方案是对余额进行单行原子更新。谢谢大家的反馈。我将进一步研究触发器。Pling。也许我的修改(编辑了我的文章)会给你更多的想法。
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)
INSERT INTO `txns`
  (`account_id`, `prev_balance`, `txn_type`, `new_balance`,  `amount`, `description`)
SELECT *
FROM (
 SELECT
  t.account_id, t.new_balance, $txn_type, t.new_balance - $amount, $amount, $description
 FROM `txns` t
 WHERE t.account_id = '$account'
 ORDER BY txn_id desc
 LIMIT 1
)
WHERE new_balance - $amount > 0