用SUM更新表

时间:2010-11-15 06:54:55

标签: mysql sql-update

我有一张名为pettycash的表

CREATE TABLE `pettycash` (
  `pc_id` int(7) NOT NULL AUTO_INCREMENT,
  `pc_date` date NOT NULL,
  `pc_in` double(13,2) DEFAULT '0.00',
  `pc_out` double(13,2) DEFAULT '0.00',
  `pc_bal` double(13,2) DEFAULT '0.00',
  `pc_ref` varchar(95) DEFAULT NULL,
  `pc_user` varchar(65) DEFAULT NULL,
  `pc_terminal` varchar(128) DEFAULT NULL,
  `pc_void` tinyint(1) DEFAULT '0',
   PRIMARY KEY (`pc_id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

此表存储有关小额现金管理的数据,但我有一个简单的问题,即在特定日期更新余额。每次我插入i运行以下查询:

UPDATE pettycash a SET pc_bal=SUM(pc_in-pc_out) WHERE pc_id=" & newID 

但问题出现的时候有人来发布过去的日期交易。上述查询只会更新一行,而更新当前日期的其他行将具有错误的余额值。是否有查询或存储过程将更新整个表,以获得每个日期的正确余额?

1 个答案:

答案 0 :(得分:2)

Triggers可能是你想要的。但是,让它正常有效地工作将是丑陋的。如果您要在更早的日期插入行,那么最好不要在每行中存储余额;相反,使用查询或views来查找余额。要查找特定日期的余额,请将其与早期日期的行相加,并将净存款加起来,按当前交易ID进行分组:

CREATE VIEW pettybalance
  AS SELECT SUM(older.pc_in - older.pc_out) AS balance, 
            current.pc_id AS pc_id,  -- foreign key
            current.pc_date AS `date`
       FROM pettycash AS current
         JOIN pettycash AS older
           ON current.pc_date > older.pc_date 
              OR (current.pc_date = older.pc_date AND current.pc_id >= older.pc_id)
       GROUP BY current.pc_id
;

我还将older.pc_id限制为小于current.pc_id,以便修复与架构和余额计算相关的歧义。由于pc_date不是唯一的,因此您可以在给定日期拥有多个交易。如果是这样的话,每笔交易的余额应该是多少?这里我们假设具有较大ID的事务在具有较小ID但具有相同日期的事务之后出现。更正式地说,我们使用排序

  

a> b⇔a.pc_date> b.pc_date∨(a.pc_date =b.pc_date∧a.pc_id> b.pc_id)

请注意,在视图中,我们使用≥基于>的订单:

  

a≥b⇔a.pc_date> b.pc_date∨(a.pc_date =b.pc_date∧a.pc_id≥b.pc_id)

在尝试使触发器正常工作后,我建议不要尝试。由于插入/更新时内部表或行锁,您必须将余额列移动到新表,尽管这不是太繁重(将pettycash重命名为pettytransactions,创建一个新的{{ 1}}表,并创建一个名为pettybalance (balance, pc_id)的视图,而不是pettycash上的pettytransactionspettybalance。主要问题是触发器主体为每个创建或更新的行执行一次,这将导致它们非常低效。另一种方法是创建stored procedure来更新列,您可以在插入或更新后调用这些列。在获取余额而不是视图时,程序更具性能,但由于程序员需要更新余额而不是让数据库处理它,因此更加脆弱。使用视图是更清洁的设计。

pc_id

题外话

当前架构的一个问题是使用DROP PROCEDURE IF EXISTS update_balance; delimiter ;; CREATE PROCEDURE update_balance (since DATETIME) BEGIN DECLARE sincebal DECIMAL(10,2); SET sincebal = ( SELECT pc_bal FROM pettycash AS pc WHERE pc.pc_date < since ORDER BY pc.pc_date DESC, pc.pc_id DESC LIMIT 1 ); IF ISNULL(sincebal) THEN SET sincebal=0.0; END IF; UPDATE pettycash AS pc SET pc_bal=( SELECT sincebal+SUM(net) FROM ( SELECT pc_id, pc_in - pc_out AS net, pc_date FROM pettycash WHERE since <= pc_date ) AS older WHERE pc.pc_date > older.pc_date OR (pc.pc_date = older.pc_date AND pc.pc_id >= older.pc_id) ) WHERE pc.pc_date >= since; END;; delimiter ; 来存储货币值。由于如何表示浮点数,在基数10中精确的数字(即,没有重复的十进制表示)并不总是精确为浮点数。例如,0.01(在基数10中)将更接近0.009999999776482582 ...或0.0100000000000000002081668 ....存储时。它更像是基数3中的1/3是“0.1”但是0.333333 ....在基数10中。您应该使用Decimal类型而不是Float

Float

如果使用视图,请删除ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2); ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2); 。如果使用存储过程来更新pettycash.pc_bal,也应该更改它。