防止mysql中的值变为负值的最佳方法
我们有一个表,通过在该表中记录交易来维护账户余额。i、 e.最近一行是账户余额 在记录提款时,我们希望确保余额永远不会为负。我们提出的解决方案类似于:防止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
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