插入总计

时间:2016-04-15 16:40:14

标签: mysql database-trigger

在MySQL事务数据库中插入运行总计的问题。需要你的帮助解决方案和意见。我的表的表结构是,

create table `wtacct` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `ACCOUNT_NO` varchar(16),
  `AMOUNT` float(16,2),
  `BALANCE` float(16,2)
);

[请注意已删除其他字段以使其成为简单示例]

我正在做交易,

  • 来自帐户1001和
  • 的10美元博士
  • Cr 10 USD to account 2002

插入查询

INSERT INTO wtacct (ID, ACCOUNT_NO, AMOUNT, BALANCE) 
VALUES ('', 1001, -10, 100), ('', 2002, 10, 5000);

我希望平衡为, 账户平衡编号1001 =上次交易账户余额1001 - 10.

我的解决方案和限制

解决方案1 ​​

在insert语句中将子查询置于余额字段中:

select balance from wtacct where account_no=1001 and id in(select max(id) from wtacct where account_no=1001)

限制:Mysql不支持插入数据的相同表选择查询(wtacct)(wtacct)。

解决方案2

使用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中没有记录,因此选择查询不会返回第一笔交易的任何记录。

解决方案3

在变量中取平衡并在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查询执行。

解决方案4

插入然后更新余额

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函数

解决方案5

在insert语句中使用触发器。在触发器中,将更新余额,计算最后的交易价值和插入金额。

限制:触发器不支持事务行为,可能会失败。

请就上述解决方案提出解决方案和意见。请注意,在上面的示例中,它们可能是一些语法错误/错误。请忽略它们。

2 个答案:

答案 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)然后选择并查看结果。