我有一张名为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
但问题出现的时候有人来发布过去的日期交易。上述查询只会更新一行,而更新当前日期的其他行将具有错误的余额值。是否有查询或存储过程将更新整个表,以获得每个日期的正确余额?
答案 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
上的pettytransactions
和pettybalance
。主要问题是触发器主体为每个创建或更新的行执行一次,这将导致它们非常低效。另一种方法是创建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
,也应该更改它。