2025-11-02 14:36:58 +01:00
# Modélisation d'un système bancaire
2025-11-02 09:24:15 +01:00
2025-11-05 07:43:14 +01:00
Une banque locale souhaite informatiser la gestion de ses titulaires et de leurs comptes.
Vous êtes chargé(e) de concevoir et d’ implémenter le schéma relationnel de base permettant de gérer :
1. Les clients de la banque,
2. Les comptes bancaires,
3. Le lien entre les titulaires et les comptes.
2025-11-05 13:47:17 +01:00
4. Les dépots et les retraits d'argent.
5. Les virements entre compte.
6. Les devises et les taux de change.
2025-11-05 07:43:14 +01:00
L'objectif de ces travaux pratiques est de :
2025-11-02 14:36:58 +01:00
- Concevoir un **modèle relationnel ** à partir d’ un scénario réaliste.
- Utiliser les **contraintes d’ intégrité ** pour garantir la cohérence des données.
- Manipuler des **jointures ** et des **relations n– n ** .
- Comprendre la notion d’ **héritage logique** en base de données.
2025-11-05 07:43:14 +01:00
La diffusion de cette application est internationale, vous vous efforcerez d'utiliser des termes anglais pour nommer les entités et les propriétés. Réferrez pour cela vous au [glossaire ](banque.glossaire.md )
2025-11-02 14:36:58 +01:00
2025-11-05 07:43:14 +01:00
Pour les entités vous utiliserez le singuler et écrirez le tout en minuscule.
2025-11-02 14:36:58 +01:00
## 1. Les titulaires
2025-11-05 07:43:14 +01:00
Un clients de la banque est appelé un titulaire. Il peut être une personne physique ou une entreprise.
2025-11-02 14:36:58 +01:00
### 1.1 Exemple de données
- une personne nommée _ Françoise Zanetti _ , née le 12 avril 1995.
2025-11-03 13:42:34 +01:00
- une entreprise nommée _ Boulangerie de Valorgue _ , créée le 19 août 2014, numéro d’ immatriculation FR19803269968.
2025-11-02 14:36:58 +01:00
### 1.2 Analyse
- Quelles informations faut-il conserver pour tous les titulaires ?
- Quelles informations sont spécifiques à chaque type de titulaire ?
- Comment représenter cette distinction en base relationnelle ?
> [!TIP]
> Indice : on peut utiliser une table abstraite `holder`, puis des tables `person` et `company` qui héritent logiquement de celle-ci.
### 1.3 Contraintes à respecter
- Chaque `person` ou `company` doit correspondre à exactement un seul `holder` .
- La suppression d’ un `holder` doit supprimer automatiquement la ligne correspondante dans `person` ou `company` .
- Le type doit être contraint à `'PERSON'` ou `'COMPANY'` .
### 1.4 Vérifications
2025-11-03 13:42:34 +01:00
- Lister tous les titulaires. Pour réutiliser rapidement la requête enregistrer la dans une vue.
- Supprimer un titulaire, vérifier que cela supprime l'individu ou la société correspondante.
### 1.5 Pour aller plus loin
La banque souhaite désormais que toute personne titulaire d’ un compte ait au moins 15 ans à la date de création de sa fiche. Il n'y a pas de restriction sur l'âge de la société.
2025-11-02 14:36:58 +01:00
2025-11-03 13:42:34 +01:00
### 1.6 Conclusion
2025-11-02 14:36:58 +01:00
2025-11-03 13:42:34 +01:00
#### Pourquoi séparer `person` et `company` ?
2025-11-02 14:36:58 +01:00
2025-11-03 13:42:34 +01:00
Parce que leurs attributs diffèrent (nom/prénom vs raison sociale).
Cela évite les **colonnes inutiles ** et permet des **contraintes spécifiques ** à chaque type.
2025-11-02 14:36:58 +01:00
2025-11-03 13:42:34 +01:00
#### Quelle contrainte empêche d’ insérer une person sans holder ?
2025-11-02 14:36:58 +01:00
2025-11-03 13:42:34 +01:00
La clé étrangère `references holder(id)` dans `person` .
2025-11-02 14:36:58 +01:00
2025-11-05 16:25:35 +01:00
```mermaid
erDiagram
person {
bigint id PK
text firstname
text lastname
date birthdate
}
company {
bigint id PK
text registration_numer
date creation_date
}
2025-11-06 06:51:21 +01:00
2025-11-05 16:25:35 +01:00
holder {
bigint id PK
int person_id FK
}
2025-11-06 06:51:21 +01:00
%% Relations
2025-11-05 16:25:35 +01:00
2025-11-06 07:22:45 +01:00
person ||--|| holder : is
company ||--|| holder : is
2025-11-05 16:25:35 +01:00
```
2025-11-02 14:36:58 +01:00
## 2. Les comptes
2025-11-02 16:21:22 +01:00
- Chaque titulaire peut détenir un ou plusieurs compte.
- Un compte bancaire doit pouvoir appartenir à un ou plusieurs titulaires (compte individuel / compte joint).
2025-11-05 07:43:14 +01:00
- Chaque compte dispose d’ un numéro de compte (_account number_) unique, d’ un solde (_balance_) et d'une date d'ouverture.
2025-11-02 16:21:22 +01:00
- Le solde des comptes ne peuvent être négatifs.
2025-11-03 07:06:56 +01:00
- Dans le cas d'un compte joint, les parts (_share_) de propriété d'un compte doivent pouvoir être précisées.
2025-11-02 16:21:22 +01:00
### 2.1 Exemple de données
- Créez un compte individuel pour Françoise Zanetti.
- Ajouter un nouveau titulaire : Justin Hébrard né le 11/03/1993.
2025-11-03 13:42:34 +01:00
- Créez un compte joint à 50/50 pour Françoise et Justin.
2025-11-02 20:50:39 +01:00
## 3. L'intégrité des données
2025-11-03 13:42:34 +01:00
Lorsque l’ on tente d'insèrer une nouvelle personne qui n'a pas l'âge requis. La ligne dans `holder` est d'abord créée, puis l'insertion dans `person` échoue à cause de la vérification d'âge. Mais la ligne du titulaire est toujours présente **sans être rattachée ** à une personne. On parle alors d'enregistrement **orphelin ** .
2025-11-02 20:50:39 +01:00
Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première n’ est pas annulée automatiquement.
2025-11-03 13:42:34 +01:00
Réalisez l’ insertion d’ un titulaire complet (dans holder et person) à l’ aide d’ une **transaction ** .
2025-11-02 20:50:39 +01:00
Testez le cas où la contrainte d’ âge échoue et vérifiez que rien n’ est inséré dans holder.
2025-11-03 13:42:34 +01:00
```sql
begin;
...
commit;
```
2025-11-02 20:50:39 +01:00
2025-11-06 06:51:21 +01:00
```mermaid
erDiagram
person {
bigint id PK
text firstname
text lastname
date birthdate
}
company {
bigint id PK
text registration_numer
date creation_date
}
bank {
bigint id PK
}
holder {
bigint id PK
date creation_date
}
account {
bigint id PK
date creation_date
2025-11-06 07:17:31 +01:00
decimal balance
2025-11-06 06:51:21 +01:00
text currency_code FK
}
account_holder {
bigint account_id FK
bigint holder_id FK
2025-11-06 07:17:31 +01:00
decimal share
2025-11-06 06:51:21 +01:00
}
transaction {
bigint id PK
date transaction_date
decimal amount
}
currency {
text code PK
}
currency {
text code PK
}
2025-11-06 06:58:42 +01:00
exchange_rate {
date date PK
text currency_code PK/FK
decimal rate
}
2025-11-06 06:51:21 +01:00
2025-11-06 07:13:27 +01:00
transaction {
bigint id PK
date date
decimal amount
}
2025-11-06 07:17:31 +01:00
operation {
bigint id PK
bigint transaction_id
text source_account
text target_account
decimal amount
text direction
}
2025-11-06 06:58:42 +01:00
%% Relations
2025-11-06 06:51:21 +01:00
2025-11-06 07:22:45 +01:00
person ||--|| holder : a
company ||--|| holder : a
bank ||--|| holder : a
holder }|--|{ account_holder : a
account_holder }|--|{ account : a
currency ||--|{ account : a
exchange_rate }o--|| currency : a
transaction }o--|| operation : a
operation }o--|| account : a
operation }o--|| account : a
2025-11-06 06:51:21 +01:00
```
2025-11-03 13:42:34 +01:00
## 4. Procédure stockée
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Pour fiabiliser le process et être sûr que l'execution s'effectue toujours dans une transaction,
nous allons encapsuler la création d’ un titulaire dans une **procédure stockée ** .
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
#### Créez une **procédure stockée** appelée `create_person_holder`.
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Cette procédure prend en paramètre :
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
* le prénom (`p_firstname text` )
* le nom (`p_lastname text` )
* la date de naissance (`p_birthdate date` )
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Cette procédure doit :
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
* Vérifier que la personne a **au moins 15 ans ** ;
* Créer automatiquement un enregistrement dans `holder` de type `'PERSON'` ;
* Récupérer l’ identifiant généré et créer la fiche dans `person` ;
* Afficher un message de confirmation.
2025-11-02 20:50:39 +01:00
3. Si la personne a moins de 15 ans :
2025-11-03 13:42:34 +01:00
* La procédure doit **refuser la création ** et afficher une erreur claire.
2025-11-02 20:50:39 +01:00
En PL/pgSQL les **procédures ** s’ écrivent :
2025-11-05 07:43:14 +01:00
```sql
create or replace procedure nom_procedure(paramètres)
language plpgsql
as $$
declare
-- variables locales
begin
-- instructions SQL
end;
$$;
```
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Pour afficher un message de réussite :
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
```sql
raise notice 'message %', variable;
```
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Vous pouvez déclencher une erreur métier à l’ aide de :
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
```sql
raise exception 'message';
```
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
Pour appeler la procédure stockée
2025-11-02 20:50:39 +01:00
```sql
2025-11-03 13:42:34 +01:00
call create_person_holder('Félicien', 'Hébrard', '1970-10-15');
2025-11-02 20:50:39 +01:00
```
>[!NOTE]
> La procédure garantit l’ **atomicité** : soit tout est créé, soit rien.
## 5. Cohérence des données
2025-11-03 13:42:34 +01:00
Écrire une requête pour vérifier la somme des parts
2025-11-02 20:50:39 +01:00
Jusqu’ à présent, nous savons créer des titulaires (`holder` ) de façon sûre.
Il est temps de leur ouvrir des **comptes bancaires ** .
Un compte doit toujours :
* être associé à **au moins un titulaire ** ,
* et la somme des parts (`share` ) des titulaires doit être exactement **égale à 1 ** (c’ est-à-dire 100 % du compte).
Nous allons donc écrire une **procédure stockée ** `create_account` qui vérifie ces règles avant d’ enregistrer les données.
1. Créez une **procédure stockée ** `create_account` qui :
2025-11-03 13:42:34 +01:00
* prend en paramètre :
* `p_iban text` ,
* `p_name text` ,
* `p_holders int[]` (tableau des identifiants de titulaires),
* `p_shares numeric[]` (tableau des parts correspondantes).
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
* vérifie que :
2025-11-02 20:50:39 +01:00
2025-11-03 13:42:34 +01:00
* le nombre d’ éléments dans `p_holders` et `p_shares` est identique ;
* la somme des parts est **exactement égale à 1 ** ;
* chaque `holder_id` existe dans la table `holder` .
2025-11-02 20:50:39 +01:00
2. Si tout est correct :
2025-11-03 13:42:34 +01:00
* crée un nouveau compte dans `account` ;
* insère les lignes correspondantes dans `account_holder` .
2025-11-02 20:50:39 +01:00
3. Si une condition échoue :
2025-11-03 13:42:34 +01:00
* la procédure doit lever une **erreur explicite ** (`RAISE EXCEPTION` ) ;
* aucune insertion ne doit être faite (transaction annulée).
2025-11-02 20:50:39 +01:00
* Les tableaux peuvent être parcourus avec une boucle :
2025-11-03 13:42:34 +01:00
```sql
for i in 1..array_length(p_holders, 1) loop
...
end loop;
```
2025-11-02 20:50:39 +01:00
* Pour vérifier la somme :
2025-11-03 13:42:34 +01:00
```sql
select sum(unnest(p_shares));
```
2025-11-02 20:50:39 +01:00
(ou additionner dans la boucle)
* Vous pouvez lever une erreur personnalisée :
2025-11-03 13:42:34 +01:00
```sql
raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme;
```
2025-11-02 20:50:39 +01:00
2025-11-03 07:06:56 +01:00
### Exemples d’ appel
2025-11-02 20:50:39 +01:00
```sql
call create_account(
'FR761234567890',
'Compte commun',
2025-11-03 07:06:56 +01:00
array[1, 5],
2025-11-02 20:50:39 +01:00
array[0.5, 0.5]
);
```
2025-11-03 07:06:56 +01:00
Crée un compte partagé 50/50 entre les titulaires 1 et 2.
2025-11-02 20:50:39 +01:00
```sql
call create_account(
'FR009999999999',
'Compte déséquilibré',
2025-11-03 07:06:56 +01:00
array[1, 5],
2025-11-02 20:50:39 +01:00
array[0.7, 0.4]
);
```
2025-11-03 07:06:56 +01:00
Doit refuser la création avec une erreur claire :
2025-11-02 20:50:39 +01:00
```
ERROR: La somme des parts (1.1000) doit être égale à 1.0000
```
2025-11-05 07:43:14 +01:00
## La double écriture comptable
Jusqu’ à présent, les dépôts et retraits modifiaient directement le solde d’ un compte.
Mais dans un vrai système bancaire ou comptable, chaque opération financière doit être enregistrée en double :
➕ Un débit sur un compte (celui qui reçoit)
➖ Un crédit sur un autre (celui qui cède)
La somme des débits doit toujours être égale à la somme des crédits.