在MySQL事务数据库中插入运行总计的问题。需要你的帮助解决方案和意见。我的表的表结构是,
create table `wtacct` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ACCOUNT_NO` varchar(16),
`AMOUNT` float(16,2),
`BALANCE` float(16,2)
);
[请注意已删除其他字段以使其成为简单示例]
我正在做交易,
插入查询
INSERT INTO wtacct (ID, ACCOUNT_NO, AMOUNT, BALANCE)
VALUES ('', 1001, -10, 100), ('', 2002, 10, 5000);
我希望平衡为, 账户平衡编号1001 =上次交易账户余额1001 - 10.
在insert语句中将子查询置于余额字段中:
select balance from wtacct where account_no=1001 and id in(select max(id) from wtacct where account_no=1001)
限制:Mysql不支持插入数据的相同表选择查询(wtacct)(wtacct)。
使用insert into select statement
insert into wtacct select '' ID, 1001 ACCOUNT_NO, -10 AMOUNT, (BALANCE-10) BALANCE where account_no=1001 and id in(select max(id) from wtacct where account_no=1001)
限制:对于第一笔交易,帐户1001的wtacct中没有记录,因此选择查询不会返回第一笔交易的任何记录。
在变量中取平衡并在insert语句中使用它。
select @balance1001 :=balance from wtacct
where account_no=1001 and id in(select max(id) from wtacct where account_no=1001)
select @balance2002 :=balance from wtacct
where account_no=2002 and id in(select max(id) from wtacct where account_no=2002)
INSERT INTO wtacct (ID, ACCOUNT_NO, AMOUNT, BALANCE)
VALUES ('', 1001, -10, @balance1001-10), ('', 2002, 10, @balance2002+10);
限制:有可能更改select和insert查询执行之间的时间平衡。还需要高成本的3查询执行。
插入然后更新余额
INSERT INTO wtacct (ID, ACCOUNT_NO, AMOUNT, BALANCE)
VALUES ('', 1001, -10, 0);
UPDATE wtacct set balance = (ifnull(Select balance from wtacct where account_no=1001 and id in(select max(id) from wtacct where id <last_insert_id() and account_no=1001),0) -10)
where id =last_insert_id() and account_no=1001
........
限制:查询费用很高。它需要4次(两次插入和2次更新)查询执行。注意last_insert_id()是php函数
在insert语句中使用触发器。在触发器中,将更新余额,计算最后的交易价值和插入金额。
限制:触发器不支持事务行为,可能会失败。
请就上述解决方案提出解决方案和意见。请注意,在上面的示例中,它们可能是一些语法错误/错误。请忽略它们。
答案 0 :(得分:0)
我没有看到列出的一个很大的限制是潜在的竞争条件,其中两行同时插入到表中。两个插件都有可能获得当前的平衡&#34;来自相同的上一行。
一个问题:您是否还有一个单独的&#34;当前余额&#34;保持当前&#34;余额&#34;的单个值的表对于每个帐户?或者你只是依靠&#34;平衡&#34;来自上一次交易。
就个人而言,我会在单独的账户余额中追踪当前的余额&#34;表。我会使用BEFORE INSERT / UPDATE触发器来维护该行中的值,并使用它来返回帐户的当前余额。
例如,我会定义一个这样的触发器,当一行插入`wtacct`表时会被触发:
CREATE TRIGGER wtacct_bi
BEFORE INSERT ON wtacct
FOR EACH ROW
BEGIN
IF NEW.amount IS NULL THEN
SET NEW.amount = 0;
END IF
;
UPDATE acct a
SET a.balance = (@new_balance := a.balance + NEW.amount)
WHERE a.account_no = NEW.account_no
;
SET NEW.balance = @new_balance
;
END$$
该触发器的设置......
CREATE TABLE acct
( account_no VARCHAR(16) NOT NULL PRIMARY KEY
, balance DECIMAL(20,2) NOT NULL DEFAULT 0
) ENGINE=InnoDB
;
CREATE TABLE wtacct
( id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT
, account_no VARCHAR(16) NOT NULL COMMENT 'FK ref acct.account_no'
, amount DECIMAL(20,2) NOT NULL
, balance DECIMAL(20,2) NOT NULL
, FOREIGN KEY FK_wtacct_acct (account_no) REFERENCES acct (account_no)
ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB
;
我使用单独的&#34;当前余额的原因&#34; table表示给定的account_no只有一个行,该行保留了该帐户的当前余额。
触发器中的UPDATE语句应该对正在更新的行获取独占锁。并且该独占锁可防止任何其他UPDATE语句同时更新同一行。 UPDATE语句的执行将添加从当前交易行插入到当前余额中的“金额”。
如果我们使用的是Oracle或PostgreSQL,我们可以使用RETURNING子句来获取分配给\&#39;余额的值。柱。
在MySQL中,我们可以使用用户定义的变量做一个不稳定的解决方法。我们要分配给列的新值首先分配给user_defined变量,然后分配给该列。
我们可以将用户定义变量的值分配给插入`wtacct`的行的`balance`列。
此方法的目的是在单语句中检索和更新当前余额,以避免任何竞争条件。
UPDATE语句定位行,获取行上的独占(X)锁定,检索当前余额(来自\#39;余额列的值),计算新的当前余额,以及将其分配回平衡\&#39;柱。然后继续保持锁定,直到事务完成。
触发器完成后,INSERT语句(最初触发触发器)继续,尝试将新行插入“wtacct”。如果失败,那么INSERT语句和触发器执行所做的所有更改都将被回滚,从而保持一致。
一旦会话发出COMMIT或ROLLBACK,就会释放`acct`中行上的独占(X)锁,其他会话可以获取`acct`中该行的锁。
答案 1 :(得分:0)
我使用MySql的存储过程
完成了它CREATE DEFINER=`root`@`%` PROCEDURE `example_add`(IN dr Int, IN cr Int)
BEGIN
DECLARE LID int;
Declare Balance decimal(16,2);
INSERT INTO example (Debit,Credit)
VALUES (dr, cr);
SET LID = LAST_INSERT_ID();
SET Balance = (select SUM(Debit) - SUM(Credit) as Balance from example);
UPDATE Example SET Balance = Balance WHERE ID = LID;
END
使用它example_add(10,0)或example_add(0,15)然后选择并查看结果。