I have a table called 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;
This table stores data about the petty cash management,but i have a simple problem of updating the balance as at a particular date. Each time i insert i run the following query:
UPDATE pettycash a SET pc_bal=SUM(pc_in-pc_out) WHERE pc_id=" & newID
but the problem comes when someone comes to post transactions for a previous date like yesterday. the above query will only update one row and the other rows of a more current date will have wrong balance values. Is there a query or a Stored Procedure that will update the whole table getting the correct balance for each date?
Triggers are probably want you want. However, getting this to work properly and efficiently will be ugly. It’s probably better not to store the balance in each row if you’re going to be inserting rows at earlier dates all that frequently; instead, use queries or views to find the balance. To find the balance on a particular date, join it with the rows for earlier dates and sum the net deposit, grouping by the current transaction ID:
I also restrict
older.pc_idto be less thancurrent.pc_idin order to fix an ambiguity relating to the schema and the balance calculation. Since thepc_dateisn’t unique, you could have multiple transactions for a given date. If that’s the case, what should the balance be for each transaction? Here we assume that a transaction with a larger ID comes after a transaction with a smaller ID but that has the same date. More formally, we use the orderingNote that in the view, we use a ≥ order based on >:
After trying to get triggers to work properly, I’m going to recommend not even trying. Due to internal table or row locks when inserting/updating, you have to move the balance column to a new table, though this isn’t too onerous (rename
pettycashtopettytransactions, create a newpettybalance (balance, pc_id)table, and create a view namedpettycashthan joinspettytransactionsandpettybalanceonpc_id). The main problem is that trigger bodies execute once for each row created or updated, which will cause them to be incredibly inefficient. An alternative would be to create a stored procedure to update columns, which you can call after inserting or updating. A procedure is more performant when getting balances than a view, but more brittle as it’s up to programmers to update balances, rather than letting the database handle it. Using a view is the cleaner design.Off-topic
A problem with the current schema is the use of
Floats to store monetary values. Due to how floating point numbers are represented, numbers that are exact in base 10 (i.e. don’t have a repeating decimal representation) aren’t always exact as floats. For example, 0.01 (in base 10) will be closer to 0.009999999776482582… or 0.0100000000000000002081668… when stored. It’s rather like how 1/3 in base 3 is “0.1” but 0.333333…. in base 10. Instead ofFloat, you should use theDecimaltype:If using a view, drop
pettycash.pc_bal. If using a stored procedure to updatepettycash.pc_bal, it too should be altered.