This commit is contained in:
2025-11-03 13:42:34 +01:00
parent 23e28fb2d8
commit 80b74d724b
3 changed files with 73 additions and 126 deletions

View File

@@ -10,18 +10,6 @@
- raison sociale, numéro dimmatriculation, date de création - raison sociale, numéro dimmatriculation, date de création
#### 1.5.1 Pourquoi séparer `person` et `company` ?
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.
#### 1.5.2 Pourquoi ne pas tout mettre dans une seule table holder ?
Cela forcerait la présence de nombreuses colonnes nulles. La séparation améliore la cohérence et la lisibilité du schéma.
#### 1.5.3 Quelle contrainte empêche dinsérer une person sans holder ?
La clé étrangère references holder(id) dans person.
### 1.6 Contôle de l'âge ### 1.6 Contôle de l'âge

185
banque.md
View File

@@ -1,16 +1,11 @@
# Modélisation d'un système bancaire # Modélisation d'un système bancaire
## Objectifs
- Concevoir un **modèle relationnel** à partir dun scénario réaliste. - Concevoir un **modèle relationnel** à partir dun scénario réaliste.
- Utiliser les **contraintes dintégrité** pour garantir la cohérence des données. - Utiliser les **contraintes dintégrité** pour garantir la cohérence des données.
- Manipuler des **jointures** et des **relations nn**. - Manipuler des **jointures** et des **relations nn**.
- Comprendre la notion d**héritage logique** en base de données. - Comprendre la notion d**héritage logique** en base de données.
## Contexte Une banque locale souhaite informatiser la gestion de ses titulaires et de leurs comptes.
Une banque locale souhaite informatiser la gestion de ses comptes et de leurs titulaires.
Chaque compte peut appartenir à une ou plusieurs personnes physiques ou morales.
Vous êtes chargé(e) de concevoir et dimplémenter le schéma relationnel de base permettant de gérer : Vous êtes chargé(e) de concevoir et dimplémenter le schéma relationnel de base permettant de gérer :
@@ -25,7 +20,7 @@ Un titulaire (_holder_) peut être une personne physique (_person_) ou une entre
### 1.1 Exemple de données ### 1.1 Exemple de données
- une personne nommée _Françoise Zanetti_, née le 12 avril 1995. - une personne nommée _Françoise Zanetti_, née le 12 avril 1995.
- une entreprise nommée _Boulangerie de Valorgue_, créée le 19/08/2014, numéro dimmatriculation FR19803269968. - une entreprise nommée _Boulangerie de Valorgue_, créée le 19 août 2014, numéro dimmatriculation FR19803269968.
### 1.2 Analyse ### 1.2 Analyse
@@ -44,19 +39,23 @@ Un titulaire (_holder_) peut être une personne physique (_person_) ou une entre
### 1.4 Vérifications ### 1.4 Vérifications
Lister tous les titulaires. - 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.
Supprimer un titulaire, vérifier que cela supprime l'individu ou la société correspondante. ### 1.5 Pour aller plus loin
### 1.5 Réflexion La banque souhaite désormais que toute personne titulaire dun 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é.
1. Pourquoi séparer `person` et `company` ? ### 1.6 Conclusion
2. Pourquoi ne pas tout mettre dans une seule table holder ?
3. Quelle contrainte empêche dinsérer une person sans holder ?
### 1.6 Pour aller plus loin #### Pourquoi séparer `person` et `company` ?
La banque souhaite désormais que toute personne titulaire dun compte ait au moins 15 ans à la date de création de sa fiche. 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.
#### Quelle contrainte empêche dinsérer une person sans holder ?
La clé étrangère `references holder(id)` dans `person`.
## 2. Les comptes ## 2. Les comptes
@@ -70,44 +69,46 @@ La banque souhaite désormais que toute personne titulaire dun compte ait au
- Créez un compte individuel pour Françoise Zanetti. - Créez un compte individuel pour Françoise Zanetti.
- Ajouter un nouveau titulaire : Justin Hébrard né le 11/03/1993. - Ajouter un nouveau titulaire : Justin Hébrard né le 11/03/1993.
- Créez un compte joint pour Françoise et Justin. - Créez un compte joint à 50/50 pour Françoise et Justin.
## 3. L'intégrité des données ## 3. L'intégrité des données
Lorsque lon 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 holder est toujours présente sans être rattachée à une personne. Lorsque lon 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**.
Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première nest pas annulée automatiquement. Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première nest pas annulée automatiquement.
Réalisez linsertion dun titulaire complet (dans holder et person) à laide dune transaction. Réalisez linsertion dun titulaire complet (dans holder et person) à laide dune **transaction**.
Testez le cas où la contrainte dâge échoue et vérifiez que rien nest inséré dans holder. Testez le cas où la contrainte dâge échoue et vérifiez que rien nest inséré dans holder.
```sql
begin;
...
commit;
```
## 4. Procédure stockée ## 4. Procédure stockée
Vous avez remarqué quen ajoutant une contrainte sur lâge minimum du titulaire, une insertion invalide dans `person` peut échouer **après** la création du `holder`. Pour fiabiliser le process et être sûr que l'execution s'effectue toujours dans une transaction,
Cela laisse un **titulaire orphelin** sans fiche personnelle. nous allons encapsuler la création dun titulaire dans une **procédure stockée**.
Dans un système bancaire réel, ce type dincohérence est inacceptable. #### Créez une **procédure stockée** appelée `create_person_holder`.
Nous allons donc **encapsuler la création dun titulaire dans une procédure stockée** pour garantir la cohérence et la simplicité dusage.
1. Créez une **procédure stockée** appelée `create_person_holder` Cette procédure prend en paramètre :
* qui prend en paramètre : * le prénom (`p_firstname text`)
* le nom (`p_lastname text`)
* la date de naissance (`p_birthdate date`)
* le prénom (`p_firstname text`) Cette procédure doit :
* le nom (`p_lastname text`)
* la date de naissance (`p_birthdate date`)
2. Cette procédure doit : * Vérifier que la personne a **au moins 15 ans** ;
* Créer automatiquement un enregistrement dans `holder` de type `'PERSON'`;
* Vérifier que la personne a **au moins 15 ans** ; * Récupérer lidentifiant généré et créer la fiche dans `person` ;
* Créer automatiquement un enregistrement dans `holder` (type = `'PERSON'`) ; * Afficher un message de confirmation.
* Récupérer lidentifiant généré et créer la fiche dans `person` ;
* Afficher un message de confirmation avec `RAISE NOTICE`.
3. Si la personne a moins de 15 ans : 3. Si la personne a moins de 15 ans :
* La procédure doit **refuser la création** et afficher une erreur claire (par `RAISE EXCEPTION`). * La procédure doit **refuser la création** et afficher une erreur claire.
En PL/pgSQL les **procédures** sécrivent : En PL/pgSQL les **procédures** sécrivent :
@@ -123,22 +124,22 @@ En PL/pgSQL les **procédures** sécrivent :
$$; $$;
``` ```
Vous pouvez intercepter une erreur métier à laide de :
```sql
raise exception 'message';
```
Pour afficher un message de réussite : Pour afficher un message de réussite :
```sql ```sql
raise notice 'message %', variable; raise notice 'message %', variable;
``` ```
Pour appeler une procédure stockée Vous pouvez déclencher une erreur métier à laide de :
```sql ```sql
call create_person_holder('Alice', 'Martin', '1990-03-15'); raise exception 'message';
```
Pour appeler la procédure stockée
```sql
call create_person_holder('Félicien', 'Hébrard', '1970-10-15');
``` ```
>[!NOTE] >[!NOTE]
@@ -146,18 +147,7 @@ call create_person_holder('Alice', 'Martin', '1990-03-15');
## 5. Cohérence des données ## 5. Cohérence des données
Écrire une requête pour vérifier la somme Écrire une requête pour vérifier la somme des parts
Excellent 👌 — on va continuer dans la même logique de rigueur comptable et de progressivité pédagogique.
Cette étape introduira une **seconde procédure stockée**, dédiée à la création de comptes, avec **contrôle des titulaires associés et de leurs parts (shares)**.
---
# 🧩 TP Banque Étape 5 : Créer un compte bancaire de manière cohérente
---
## 🎯 Objectif
Jusquà présent, nous savons créer des titulaires (`holder`) de façon sûre. Jusquà présent, nous savons créer des titulaires (`holder`) de façon sûre.
Il est temps de leur ouvrir des **comptes bancaires**. Il est temps de leur ouvrir des **comptes bancaires**.
@@ -169,81 +159,52 @@ Un compte doit toujours :
Nous allons donc écrire une **procédure stockée** `create_account` qui vérifie ces règles avant denregistrer les données. Nous allons donc écrire une **procédure stockée** `create_account` qui vérifie ces règles avant denregistrer les données.
---
## 📘 Contexte
Les tables concernées sont les suivantes :
```sql
create table account (
id serial primary key,
iban text not null unique,
name text not null,
opened_on date not null default current_date
);
create table account_holder (
account_id int not null references account(id) on delete cascade,
holder_id int not null references holder(id) on delete restrict,
share numeric(5,4) not null check (share > 0 and share <= 1),
primary key (account_id, holder_id)
);
```
👉 La table `account_holder` relie un compte à un ou plusieurs titulaires, avec la part de chacun.
---
## 🧱 Travail demandé
1. Créez une **procédure stockée** `create_account` qui : 1. Créez une **procédure stockée** `create_account` qui :
* prend en paramètre : * prend en paramètre :
* `p_iban text`, * `p_iban text`,
* `p_name text`, * `p_name text`,
* `p_holders int[]` (tableau des identifiants de titulaires), * `p_holders int[]` (tableau des identifiants de titulaires),
* `p_shares numeric[]` (tableau des parts correspondantes). * `p_shares numeric[]` (tableau des parts correspondantes).
* vérifie que :
* le nombre déléments dans `p_holders` et `p_shares` est identique ; * vérifie que :
* la somme des parts est **exactement égale à 1** ;
* chaque `holder_id` existe dans la table `holder`. * 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`.
2. Si tout est correct : 2. Si tout est correct :
* crée un nouveau compte dans `account` ; * crée un nouveau compte dans `account` ;
* insère les lignes correspondantes dans `account_holder`. * insère les lignes correspondantes dans `account_holder`.
3. Si une condition échoue : 3. Si une condition échoue :
* la procédure doit lever une **erreur explicite** (`RAISE EXCEPTION`) ; * la procédure doit lever une **erreur explicite** (`RAISE EXCEPTION`) ;
* aucune insertion ne doit être faite (transaction annulée). * aucune insertion ne doit être faite (transaction annulée).
* Les tableaux peuvent être parcourus avec une boucle : * Les tableaux peuvent être parcourus avec une boucle :
```sql ```sql
for i in 1..array_length(p_holders, 1) loop for i in 1..array_length(p_holders, 1) loop
... ...
end loop; end loop;
``` ```
* Pour vérifier la somme : * Pour vérifier la somme :
```sql ```sql
select sum(unnest(p_shares)); select sum(unnest(p_shares));
``` ```
(ou additionner dans la boucle) (ou additionner dans la boucle)
* Vous pouvez lever une erreur personnalisée : * Vous pouvez lever une erreur personnalisée :
```sql ```sql
raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme; raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme;
``` ```
---
### Exemples dappel ### Exemples dappel

View File

@@ -369,5 +369,3 @@ END;
$$; $$;