J'utilise PostgreSQL mais je pense que la plupart des bases de données haut de gamme doivent avoir des capacités similaires, et de plus, que leurs solutions peuvent m'inspirer des solutions, alors ne considérez pas cette spécification PostgreSQL.
Je sais que je ne suis pas le premier à essayer de résoudre ce problème, donc je pense qu'il vaut la peine de le demander ici, mais j'essaie d'évaluer les coûts de modélisation des données comptables de sorte que chaque transaction soit fondamentalement équilibrée. Les données comptables sont en annexe uniquement. La contrainte globale (écrite en pseudo-code) ici pourrait ressembler à peu près à:
CREATE TABLE journal_entry (
id bigserial not null unique, --artificial candidate key
journal_type_id int references journal_type(id),
reference text, -- source document identifier, unique per journal
date_posted date not null,
PRIMARY KEY (journal_type_id, reference)
);
CREATE TABLE journal_line (
entry_id bigint references journal_entry(id),
account_id int not null references account(id),
amount numeric not null,
line_id bigserial not null unique,
CHECK ((sum(amount) over (partition by entry_id) = 0) -- this won't work
);
De toute évidence, une telle contrainte de vérification ne fonctionnera jamais. Il fonctionne par ligne et peut vérifier la totalité de la base de données. Il échouera donc toujours et sera lent à le faire.
Ma question est donc quelle est la meilleure façon de modéliser cette contrainte? Jusqu'à présent, j'ai examiné deux idées. Vous vous demandez si ce sont les seuls, ou si quelqu'un a un meilleur moyen (autre que de le laisser au niveau de l'application ou d'un proc stocké).
- Je pourrais emprunter une page du concept du monde comptable de la différence entre un livre d'entrée original et un livre d'entrée finale (journal général vs grand livre). À cet égard, je pourrais modéliser cela comme un tableau de lignes de journal attaché à l'entrée de journal, appliquer la contrainte sur le tableau (en termes PostgreSQL, sélectionnez sum (amount) = 0 dans unnest (je.line_items). Un déclencheur pourrait se développer et enregistrez-les dans une table d'éléments de ligne, où les contraintes de colonnes individuelles pourraient plus facilement être appliquées, et où les index, etc. pourraient être plus utiles.
- Je pourrais essayer de coder un déclencheur de contrainte qui imposerait cela par transaction avec l'idée que la somme d'une série de 0 sera toujours 0.
Je les compare à l'approche actuelle consistant à appliquer la logique dans une procédure stockée. Le coût de la complexité est mis en balance avec l'idée que la preuve mathématique des contraintes est supérieure aux tests unitaires. L'inconvénient majeur de # 1 ci-dessus est que les types sous forme de tuples sont l'un de ces domaines dans PostgreSQL où l'on rencontre régulièrement des comportements incohérents et des changements d'hypothèses.J'espère donc même que le comportement dans ce domaine pourrait changer avec le temps. Concevoir une future version sûre n'est pas si facile.
Existe-t-il d'autres moyens de résoudre ce problème qui atteindront des millions d'enregistrements dans chaque table? Suis-je en train de manquer quelque chose? Y a-t-il un compromis que j'ai manqué?
En réponse au point de Craig ci-dessous sur les versions, au minimum, cela devra fonctionner sur PostgreSQL 9.2 et supérieur (peut-être 9.1 et supérieur, mais nous pouvons probablement aller avec 9.2 directement).