Déclencheurs sont probablement que vous voulez. Cependant, faire en sorte que cela fonctionne correctement et efficacement sera moche. Il est probablement préférable de ne pas stocker le solde dans chaque ligne si vous insérez fréquemment des lignes à des dates antérieures ; à la place, utilisez des requêtes ou vues pour trouver l'équilibre. Pour trouver le solde à une date particulière, joignez-le aux lignes des dates antérieures et additionnez le dépôt net, en regroupant par l'ID de transaction actuel :
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
;
Je limite également older.pc_id
être inférieur à current.pc_id
afin de lever une ambiguïté relative au schéma et au calcul du solde. Depuis le pc_date
n'est pas unique, vous pourriez avoir plusieurs transactions pour une date donnée. Si tel est le cas, quel doit être le solde de chaque transaction ? Ici, nous supposons qu'une transaction avec un ID plus grand vient après une transaction avec un ID plus petit mais qui a la même date. Plus formellement, nous utilisons la commande
Notez que dans la vue, nous utilisons un ordre ≥ basé sur> :
Après avoir essayé de faire fonctionner correctement les déclencheurs, je vais recommander de ne même pas essayer. En raison de verrous internes de table ou de ligne lors de l'insertion/de la mise à jour, vous devez déplacer la colonne de solde vers une nouvelle table, bien que ce ne soit pas trop onéreux (renommer pettycash
aux pettytransactions
, créez un nouveau pettybalance (balance, pc_id)
table et créez une vue nommée pettycash
que rejoint pettytransactions
et pettybalance
sur pc_id
). Le principal problème est que les corps de déclencheur s'exécutent une fois pour chaque ligne créée ou mise à jour, ce qui les rendra incroyablement inefficaces. Une alternative serait de créer une procédure stockée
pour mettre à jour les colonnes, que vous pouvez appeler après l'insertion ou la mise à jour. Une procédure est plus performante lors de l'obtention des soldes qu'une vue, mais plus fragile car c'est aux programmeurs de mettre à jour les soldes, plutôt que de laisser la base de données le gérer. L'utilisation d'une vue est la conception la plus propre.
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 ;
Hors sujet
Un problème avec le schéma actuel est l'utilisation de Float
s pour stocker des valeurs monétaires. En raison de la façon dont les nombres à virgule flottante sont représentés, les nombres qui sont exacts en base 10 (c'est-à-dire qui n'ont pas de représentation décimale répétitive) ne sont pas toujours exacts en tant que flottants. Par exemple, 0,01 (en base 10) sera plus proche de 0,009999999776482582... ou 0,010000000000000002081668... lorsqu'il est stocké. C'est un peu comme si 1/3 en base 3 est "0,1" mais 0,333333... en base 10. Au lieu de Float
, vous devez utiliser le Decimal
saisissez :
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);
Si vous utilisez une vue, supprimez pettycash.pc_bal
. Si vous utilisez une procédure stockée pour mettre à jour pettycash.pc_bal
, elle aussi doit être modifiée.