Conception de la base de données: Calcul du solde du compte
Comment puis-je concevoir la base de données pour calculer le solde du compte?
1) Actuellement, je calcule le solde du compte à partir de la table de transaction Dans ma table de transaction, j'ai "description " et" montant " etc..
J'additionnerais alors toutes les valeurs" montant " et cela déterminerait le solde du compte de l'utilisateur.
J'ai montré ceci à mon ami et il a dit que ce n'est pas une bonne solution, quand ma base de données va ralentir???? Il a dit que je devrais créer une table séparée pour stockez le solde calculé du compte. Si cela a été fait, je devrai maintenir deux tables, et c'est risqué, la table de solde du compte pourrait être désynchronisée.
Une suggestion?
EDIT : OPTION 2: dois-je ajouter une colonne supplémentaire à mes tables de transactions "solde". maintenant, je n'ai pas besoin de parcourir plusieurs lignes de données pour effectuer mon calcul.
Exemple John achète 100 $ crédit, il dette 60$, Il ajoute ensuite 200 $ crédit.
Montant $ 100, Solde $ 100.
Montant - 60$, solde 40$.
Montant $ 200, Solde $ 240.
9 réponses
Un problème séculaire qui n'a jamais été élégamment résolu.
Tous les paquets bancaires avec lesquels j'ai travaillé stockent le solde avec l'entité du compte. Calcul à la volée à partir de l'histoire du mouvement est impensable.
La bonne façon est:
- la table de mouvement a une ' ouverture solde ' transaction pour chaque compte. Vous aurez besoin dans quelques année, quand vous besoin de déplacer les vieux mouvements hors de la tableau de mouvement actif vers un historique table.
- Le entité de compte a un solde champ
- Il y a un déclencheur sur le mouvement table qui met à jour le compte soldes des comptes crédités et débités. Évidemment, il a un engagement contrôle. Si vous ne pouvez pas avoir de déclencheur, il doit y avoir un module unique qui écrit les mouvements sous contrôle d'engagement
- vous avez un programme de "filet de sécurité" vous peut fonctionner hors ligne, qui recalcule tous les soldes et affichages (et éventuellement corrige) erroné équilibre. C'est très utile pour test.
Certains systèmes stockent tous les mouvements sous forme de nombres positifs et expriment le crédit / débit en inversant les champs de/vers ou avec un drapeau. Personnellement, je préfère un champ de crédit, un champ de débit et un montant signé, ce qui rend les inversions beaucoup plus faciles à suivre.
Notez que ces méthodes s'appliquent à la fois aux espèces et aux titres.
Les transactions sur titres peuvent être beaucoup plus délicates, en particulier pour les actions d'entreprise, vous devrez transaction qui met à jour un ou plusieurs soldes de trésorerie de l'acheteur et du vendeur, leurs soldes de position de sécurité et éventuellement le courtier/dépositaire.
Vous devez stocker le solde du compte courant et le tenir à jour en tout temps. La table de transaction est juste un enregistrement de ce qui s'est passé dans le passé et ne devrait pas être utilisée à haute fréquence juste pour récupérer le solde actuel. Considérez que de nombreuses requêtes ne veulent pas seulement des soldes, elles veulent filtrer,trier et regrouper par elles, etc. La pénalité de performance consistant à additionner chaque transaction que vous avez créée au milieu de requêtes complexes paralyserait même une base de données modeste taille.
Toutes les mises à jour de cette paire de tables doivent être dans une transaction et doivent s'assurer que tout reste synchronisé (et que le compte ne dépasse jamais sa limite) ou que la transaction est annulée. Comme mesure supplémentaire, vous pouvez exécuter des requêtes d'audit qui vérifient cela périodiquement.
Une solution courante à ce problème consiste à maintenir un solde d'ouverture mensuel (par exemple) dans un schéma d'instantané. Le calcul du solde actuel peut être effectué en ajoutant des données transactionnelles pour le mois au solde d'ouverture mensuel. Cette approche est souvent adoptée dans les paquets de comptes, en particulier lorsque vous pourriez avoir une conversion de devises et des réévaluations.
Si vous avez des problèmes avec le volume de données, vous pouvez archiver les anciens Soldes.
En outre, les soldes peuvent être utiles pour reporting si vous ne disposez pas d'un entrepôt de données externe dédié ou d'une fonction de reporting de gestion sur le système.
Bien sûr, vous devez stocker votre solde actuel avec chaque ligne, sinon il est trop lent. Pour simplifier le développement, vous pouvez utiliser des contraintes, de sorte que vous n'avez pas besoin de déclencheurs et de contrôles périodiques de l'intégrité des données. Je l'ai décrit ici dénormaliser pour appliquer les règles métier: total en cours d'exécution
Votre ami a tort et vous avez raison, et je vous conseille de ne pas changer les choses maintenant.
Si votre base de données est lente à cause de cela, et après avoir vérifié tout le reste (indexation correcte), une dénormalisation peut être utile.
Vous pouvez ensuite mettre un champ BalanceAtStartOfYear dans la table Accounts et résumer uniquement les enregistrements de cette année (ou toute approche similaire).
Mais je ne recommanderais certainement pas cette approche dès le départ.
Voici comment vous pouvez stocker votre solde d'ouverture d'une manière très simple: -
Créez une fonction de déclenchement sur la table transaction à appeler uniquement après update ou insert.
Créez une colonne ayant un nom dans la table principale du compte nommant le solde D'ouverture.
Enregistrez votre solde d'ouverture dans le tableau dans la colonne solde d'ouverture dans la table principale.
Vous n'avez même pas besoin d'utiliser l'utilisation de la langue côté serveur ce tableau de magasin simplement vous pouvez utiliser des fonctions de tableau de base de données comme disponibles dans PostgreSQL.
Lorsque vous voulez recalculer votre solde d'ouverture dans le tableau, groupez simplement votre table de transaction avec la fonction array et mettez à jour l'ensemble des données dans la table principale.
Je l'ai fait dans PostgreSQL et fonctionne bien.
Au cours de la période où votre table de transaction deviendra lourde, vous pouvez partitionner votre table de transaction sur la base de la date pour accélérer les performances. cette approche est très facile et n'a pas besoin d'utiliser de table supplémentaire qui peut ralentir les performances en rejoignant la table car une table moindre dans la jointure vous donnera des performances élevées.
C'est une conception de base de données que j'ai obtenue avec une seule table pour simplement stocker un historique des opérations/transactions. Travaille actuellement comme charme sur de nombreux petits projets.
Cela ne remplace pas une conception spécifique. C'est une solution générique qui pourrait s'adapter à la plupart des applications.
Id:int id de ligne standard
Operation_type:int type d'opération. payer, percevoir, intérêts, etc.
Source_type:int d'où l'opération se déroule. cible tableau ou catégorie: utilisateur, banque, fournisseur, etc
Source_id:int id de la source dans la base de données
Target_type:int à ce que l'opération est appliquée. tableau cible ou catégorie: utilisateur, banque, fournisseur, etc
Target_id:int id de la cible dans la base de données
Montant:décimal(19,2 signé) valeur de prix positive ou négative à par additionné
Account_balance:décimal(19,2 signé) résulter solde
Extra_value_a : decimal (19,2 signé) [c'était l'option la plus polyvalente sans utiliser de stockage de chaîne] vous pouvez stocker un nombre supplémentaire: pourcentage d'intérêt, une remise, une réduction, etc.
Created_at : horodatage
Pour source_type et target_type, il serait préférable d'utiliser un enum ou des tables appart.
Si vous voulez un solde particulier, vous pouvez simplement interroger la dernière opération triée par created_at limite descendante à 1. Vous pouvez requête par source, Cible, operation_type, etc.
Pour de meilleures performances, il est recommandé de stocker la balance actuelle dans l'objet cible requis.
Mon approche consiste à stocker les débits dans une colonne de débit, le crédit dans la colonne de crédit et lors de l'extraction des données, créer deux tableaux, Tableau de débit et de crédit. Ensuite, continuez à ajouter les données sélectionnées au tableau et faites ceci pour python:
def real_insert(arr, index, value):
try:
arr[index] = value
except IndexError:
arr.insert(index, value)
def add_array(args=[], index=0):
total = 0
if index:
for a in args[: index]:
total += a
else:
for a in args:
total += a
return total
Puis
for n in range(0, len(array), 1):
self.store.clear()
self.store.append([str(array[n][4])])
real_insert(self.row_id, n, array[n][0])
real_insert(self.debit_array, n, array[n][7])
real_insert(self.credit_array, n, array[n][8])
if self.category in ["Assets", "Expenses"]:
balance = add_array(self.debit_array) - add_array(self.credit_array)
else:
balance = add_array(self.credit_array) - add_array(self.debit_array)
Réponse Simple: Faites les trois.
Stocke le solde actuel; et dans chaque transaction stocke le mouvement et un instantané du solde actuel à ce moment - là. Cela donnerait quelque chose supplémentaire à concilier dans n'importe quel audit.
Je n'ai jamais travaillé sur les systèmes bancaires de base, mais j'ai travaillé sur les systèmes de gestion de Placements, et d'après mon expérience, c'est ainsi que cela se fait.