Spouštěče pravděpodobně chcete, co chcete. Nicméně, aby to fungovalo správně a efektivně, bude ošklivé. Pokud budete tak často vkládat řádky v dřívějších datech, bude pravděpodobně lepší neukládat zůstatek v každém řádku; místo toho použijte dotazy nebo zobrazení najít rovnováhu. Chcete-li zjistit zůstatek k určitému datu, spojte jej s řádky pro dřívější data a sečtěte čistý vklad seskupený podle aktuálního ID transakce:
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
;
Také omezuji older.pc_id
být menší než current.pc_id
aby se odstranila nejednoznačnost týkající se schématu a výpočtu zůstatku. Od pc_date
není jedinečný, můžete mít více transakcí pro dané datum. Pokud tomu tak je, jaký by měl být zůstatek u každé transakce? Zde předpokládáme, že transakce s větším ID přichází po transakci s menším ID, která má ale stejné datum. Formálněji používáme řazení
Všimněte si, že v zobrazení používáme pořadí ≥ založené na>:
Po pokusu o správné fungování spouštěčů doporučuji ani nezkoušet. Kvůli vnitřním zámkům tabulky nebo řádků při vkládání/aktualizaci musíte sloupec zůstatku přesunout do nové tabulky, i když to není příliš náročné (přejmenujte pettycash
na pettytransactions
, vytvořte nový pettybalance (balance, pc_id)
a vytvořte pohled s názvem pettycash
než se připojí k pettytransactions
a pettybalance
na pc_id
). Hlavním problémem je, že těla spouštěčů se spouštějí jednou pro každý vytvořený nebo aktualizovaný řádek, což způsobí, že budou neuvěřitelně neefektivní. Alternativou by bylo vytvoření uložené procedury
k aktualizaci sloupců, které můžete po vložení nebo aktualizaci vyvolat. Procedura je výkonnější při získávání zůstatků než pohled, ale je křehčí, protože je na programátorech, aby aktualizovali zůstatky, než aby je nechali zpracovávat databázi. Použití pohledu je čistší design.
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 ;
Mimo téma
Problémem současného schématu je použití Float
s k ukládání peněžních hodnot. Kvůli tomu, jak jsou čísla s plovoucí desetinnou čárkou reprezentována, čísla, která jsou přesná v základu 10 (tj. nemají opakující se desítkové zastoupení), nejsou vždy přesná jako čísla s plovoucí desetinnou čárkou. Například 0,01 (v základu 10) bude blíže k 0,009999999776482582... nebo 0,0100000000000000002081668... při uložení. Je to spíše jako 1/3 v základu 3 je "0,1", ale 0,333333.... v základu 10. Místo Float
, měli byste použít Decimal
typ:
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);
Pokud používáte pohled, přetáhněte pettycash.pc_bal
. Pokud používáte uloženou proceduru k aktualizaci pettycash.pc_bal
, měl by být také změněn.