banque 6
This commit is contained in:
@@ -10,18 +10,6 @@
|
|||||||
- raison sociale, numéro d’immatriculation, date de création
|
- raison sociale, numéro d’immatriculation, 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 d’insé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
185
banque.md
@@ -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 d’un scénario réaliste.
|
- 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.
|
- Utiliser les **contraintes d’intégrité** pour garantir la cohérence des données.
|
||||||
- Manipuler des **jointures** et des **relations n–n**.
|
- Manipuler des **jointures** et des **relations n–n**.
|
||||||
- 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 d’implémenter le schéma relationnel de base permettant de gérer :
|
Vous êtes chargé(e) de concevoir et d’implé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 d’immatriculation FR19803269968.
|
- une entreprise nommée _Boulangerie de Valorgue_, créée le 19 août 2014, numéro d’immatriculation 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 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é.
|
||||||
|
|
||||||
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 d’insé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 d’un 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 d’insé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 d’un 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 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 holder est toujours présente sans être rattachée à une personne.
|
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**.
|
||||||
|
|
||||||
Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première n’est pas annulée automatiquement.
|
Chaque commande SQL est exécutée indépendamment. Si la deuxième commande échoue, la première n’est pas annulée automatiquement.
|
||||||
|
|
||||||
Réalisez l’insertion d’un titulaire complet (dans holder et person) à l’aide d’une transaction.
|
Réalisez l’insertion d’un titulaire complet (dans holder et person) à l’aide d’une **transaction**.
|
||||||
Testez le cas où la contrainte d’âge échoue et vérifiez que rien n’est inséré dans holder.
|
Testez le cas où la contrainte d’âge échoue et vérifiez que rien n’est inséré dans holder.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
begin;
|
||||||
|
...
|
||||||
|
commit;
|
||||||
|
```
|
||||||
|
|
||||||
## 4. Procédure stockée
|
## 4. Procédure stockée
|
||||||
|
|
||||||
Vous avez remarqué qu’en 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 d’un titulaire dans une **procédure stockée**.
|
||||||
|
|
||||||
Dans un système bancaire réel, ce type d’incohérence est inacceptable.
|
#### Créez une **procédure stockée** appelée `create_person_holder`.
|
||||||
Nous allons donc **encapsuler la création d’un titulaire dans une procédure stockée** pour garantir la cohérence et la simplicité d’usage.
|
|
||||||
|
|
||||||
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 l’identifiant 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 l’identifiant 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 à l’aide 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 à l’aide 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 d’enregistrer les données.
|
Nous allons donc écrire une **procédure stockée** `create_account` qui vérifie ces règles avant d’enregistrer 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 d’appel
|
### Exemples d’appel
|
||||||
|
|
||||||
|
|||||||
@@ -369,5 +369,3 @@ END;
|
|||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user