banque
This commit is contained in:
@@ -35,3 +35,341 @@ create trigger trg_check_person_age
|
|||||||
before insert or update on person
|
before insert or update on person
|
||||||
for each row execute procedure check_person_age();
|
for each row execute procedure check_person_age();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into holder (type) values ('BANK')
|
||||||
|
returning holder;
|
||||||
|
|
||||||
|
insert into bank (id, name) values (1, 'Banque de l''Est');
|
||||||
|
```
|
||||||
|
|
||||||
|
La création d'un titulaire s'effectue en deux étapes :
|
||||||
|
|
||||||
|
- Créer un compte individuel pour _Françoise Zanetti_, née le 12 avril 1995.
|
||||||
|
- Créer une entreprise nommée _Boulangerie de Valorgue_, créée le 19 août 2014, numéro d’immatriculation FR19803269968.
|
||||||
|
- Ajouter un nouveau titulaire : _Justin Hébrard_ né le 11/03/1993.
|
||||||
|
|
||||||
|
|
||||||
|
#### 1.1 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`.
|
||||||
|
- 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é.
|
||||||
|
- Le type doit être contraint à `'PERSON'` ou `'COMPANY'`.
|
||||||
|
|
||||||
|
Il existe deux méthodes pour gérer le type.
|
||||||
|
|
||||||
|
1. vérifier par la commande `CHECK` la validité de la valeur.
|
||||||
|
|
||||||
|
2. utiliser une énumération `enum`.
|
||||||
|
|
||||||
|
|
||||||
|
#### 1.2 Vérifications
|
||||||
|
|
||||||
|
- 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.4 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 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
begin;
|
||||||
|
...
|
||||||
|
commit;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.5 Procédure stockée
|
||||||
|
|
||||||
|
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**.
|
||||||
|
|
||||||
|
Créez une **procédure stockée** appelée `create_person_holder`.
|
||||||
|
|
||||||
|
Cette procédure prend en paramètre :
|
||||||
|
|
||||||
|
* le prénom (`p_firstname text`)
|
||||||
|
* le nom (`p_lastname text`)
|
||||||
|
* la date de naissance (`p_birthdate date`)
|
||||||
|
|
||||||
|
Cette procédure doit :
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
3. Si la personne a moins de 15 ans :
|
||||||
|
|
||||||
|
* La procédure doit **refuser la création** et afficher une erreur claire.
|
||||||
|
|
||||||
|
|
||||||
|
>[!NOTE]
|
||||||
|
> La procédure garantit l’**atomicité** : soit tout est créé, soit rien.
|
||||||
|
|
||||||
|
### 2. Les comptes
|
||||||
|
|
||||||
|
- Le solde des comptes ne peuvent être négatifs.
|
||||||
|
- Créez un compte joint à 50/50 pour Françoise et Justin.
|
||||||
|
|
||||||
|
Écrire une requête pour vérifier la somme des parts
|
||||||
|
|
||||||
|
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 :
|
||||||
|
|
||||||
|
* prend en paramètre :
|
||||||
|
|
||||||
|
* `p_holders int[]` (tableau des identifiants de titulaires),
|
||||||
|
* `p_shares numeric[]` (tableau des parts correspondantes).
|
||||||
|
|
||||||
|
* vérifie que :
|
||||||
|
|
||||||
|
* 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 :
|
||||||
|
|
||||||
|
* crée un nouveau compte dans `account` ;
|
||||||
|
* insère les lignes correspondantes dans `account_holder`.
|
||||||
|
|
||||||
|
3. Si une condition échoue :
|
||||||
|
|
||||||
|
* la procédure doit lever une **erreur explicite** (`RAISE EXCEPTION`) ;
|
||||||
|
* aucune insertion ne doit être faite (transaction annulée).
|
||||||
|
|
||||||
|
* Les tableaux peuvent être parcourus avec une boucle :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
for i in 1..array_length(p_holders, 1) loop
|
||||||
|
...
|
||||||
|
end loop;
|
||||||
|
```
|
||||||
|
|
||||||
|
* Pour vérifier la somme :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
select sum(unnest(p_shares));
|
||||||
|
```
|
||||||
|
|
||||||
|
(ou additionner dans la boucle)
|
||||||
|
|
||||||
|
* Vous pouvez lever une erreur personnalisée :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exemples d’appel
|
||||||
|
|
||||||
|
```sql
|
||||||
|
call create_account(
|
||||||
|
'FR761234567890',
|
||||||
|
'Compte commun',
|
||||||
|
array[1, 5],
|
||||||
|
array[0.5, 0.5]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Crée un compte partagé 50/50 entre les titulaires 1 et 2.
|
||||||
|
|
||||||
|
|
||||||
|
```sql
|
||||||
|
call create_account(
|
||||||
|
'FR009999999999',
|
||||||
|
'Compte déséquilibré',
|
||||||
|
array[1, 5],
|
||||||
|
array[0.7, 0.4]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Doit refuser la création avec une erreur claire :
|
||||||
|
|
||||||
|
```
|
||||||
|
ERROR: La somme des parts (1.1000) doit être égale à 1.0000
|
||||||
|
```
|
||||||
|
### 3. Monnaies et taux de change
|
||||||
|
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into currency values ('EUR');
|
||||||
|
insert into currency values ('YEN');
|
||||||
|
```
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into exchange_rate values
|
||||||
|
('YEN', '2025-11-03', 177.57),
|
||||||
|
('YEN', '2025-11-04', 176.39),
|
||||||
|
('YEN', '2025-11-05', 176.67),
|
||||||
|
('YEN', '2025-11-06', 177.15),
|
||||||
|
('YEN', '2025-11-07', 176.99);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Séance 3 : Exploitation des données
|
||||||
|
|
||||||
|
[Utiliser la correction](banque.correction.sql) de la base de données
|
||||||
|
|
||||||
|
|
||||||
|
## 1. Vue : taux de change de la veille
|
||||||
|
|
||||||
|
Créer une vue *yesterday_exchange_rates* qui affiche pour **chaque devise** son taux de change de la veille présent dans `exchange_rate`.
|
||||||
|
|
||||||
|
Si aucun taux du jour n’existe, la ligne ne doit pas apparaître.
|
||||||
|
|
||||||
|
|
||||||
|
## 2. Fonction : dernier taux connu pour une devise et une date données
|
||||||
|
|
||||||
|
Créer une fonction *latest_exchange_rate* qui donne, pour une devise, son taux de change le plus récent :
|
||||||
|
|
||||||
|
## 3. Vue : liste détaillées des titulaires
|
||||||
|
|
||||||
|
Créer une vue *holder_details* permettant d’afficher **tous les titulaires** (banque, personne ou entreprise) sous une forme unifiée.
|
||||||
|
|
||||||
|
La vue doit contenir :
|
||||||
|
|
||||||
|
* l’identifiant du titulaire
|
||||||
|
* son type
|
||||||
|
* un champ `display_name` calculé ainsi :
|
||||||
|
|
||||||
|
* pour un titulaire de type *PERSON* : *firstname lastname*
|
||||||
|
* pour un titulaire de type *COMPANY* : *name*
|
||||||
|
* pour un titulaire de type *BANK* : *name*
|
||||||
|
|
||||||
|
## 4. Vue : liste des comptes avec devise et solde
|
||||||
|
|
||||||
|
Créer une vue *account_detail* affichant :
|
||||||
|
|
||||||
|
* l’identifiant du compte
|
||||||
|
* la date d’ouverture
|
||||||
|
* la devise
|
||||||
|
* le solde
|
||||||
|
* le nombre de titulaires du compte
|
||||||
|
|
||||||
|
## 5. Vue : comptes par titulaire
|
||||||
|
|
||||||
|
Créer une vue *holder_accounts* permettant de lister les comptes détenus par chaque titulaire, avec :
|
||||||
|
|
||||||
|
* le titulaire (id et type)
|
||||||
|
* le nom du titulaire
|
||||||
|
* l’identifiant du compte
|
||||||
|
* la part détenue (`share`)
|
||||||
|
* le solde total du compte
|
||||||
|
|
||||||
|
La vue doit fusionner les informations venant de **holder, person et company**.
|
||||||
|
|
||||||
|
## 6. Vue : opérations enrichies
|
||||||
|
|
||||||
|
Créer une vue *operation_details* affichant les opérations avec :
|
||||||
|
|
||||||
|
* la date de l’opération
|
||||||
|
* le compte impacté
|
||||||
|
* la direction (DEBIT ou CREDIT)
|
||||||
|
* le montant de l’opération
|
||||||
|
* le montant signé (crédit positif, débit négatif)
|
||||||
|
* le solde du compte **après l’opération** (bonus : fenêtre analytique)
|
||||||
|
|
||||||
|
|
||||||
|
## 7. Vue : solde converti en EUR
|
||||||
|
|
||||||
|
Créer une vue *account_balance_eur* pour afficher :
|
||||||
|
|
||||||
|
* compte
|
||||||
|
* devise d’origine
|
||||||
|
* solde original
|
||||||
|
* taux de change correspondant à la date du jour
|
||||||
|
* solde converti en EUR (solde × taux)
|
||||||
|
|
||||||
|
## 8. Vue : transactions complètes
|
||||||
|
|
||||||
|
Créer une vue *transaction_summary* affichant un regroupement par transaction :
|
||||||
|
|
||||||
|
* id de la transaction
|
||||||
|
* date
|
||||||
|
* montant total de la transaction (somme des opérations)
|
||||||
|
* liste des comptes concernés (optionnel : concaténation)
|
||||||
|
|
||||||
|
## 9. Vue : comptes en découvert imminent
|
||||||
|
|
||||||
|
Créer une vue *accounts_at_risk* listant les comptes dont le solde est inférieur à 50 (dans leur devise), ou qui auraient un solde négatif s'ils effectuaient un débit supplémentaire de 20.
|
||||||
|
|
||||||
|
> Vérification simple : `balance - 20 < 0 OR balance < 50`.
|
||||||
|
|
||||||
|
## 10. Vue : âge des personnes
|
||||||
|
|
||||||
|
Créer une vue *person_age* indiquant :
|
||||||
|
|
||||||
|
* id
|
||||||
|
* nom complet
|
||||||
|
* date de naissance
|
||||||
|
* âge en années (utiliser `age()`)
|
||||||
|
|
||||||
|
## 11. Vue : répartition des parts d’un compte
|
||||||
|
|
||||||
|
Créer une vue *account_shares* affichant :
|
||||||
|
|
||||||
|
* id du compte
|
||||||
|
* nombre de titulaires
|
||||||
|
* somme des parts
|
||||||
|
* une colonne booléenne `is_valid` vérifiant si la somme = 1
|
||||||
|
|
||||||
|
> Objectif : vérifier que les parts des comptes joints sont bien réparties.
|
||||||
|
|
||||||
|
## 12. Vue : solde par titulaire
|
||||||
|
|
||||||
|
Créer une vue *holder_total_balance* indiquant pour chaque titulaire :
|
||||||
|
|
||||||
|
* id
|
||||||
|
* type
|
||||||
|
* nom
|
||||||
|
* somme des soldes de tous ses comptes (pondérée par `share`), calcul :
|
||||||
|
`total = SUM(share × balance)`
|
||||||
|
|
||||||
|
## 13. Vue : opérations d’un compte en sens unique
|
||||||
|
|
||||||
|
Créer une vue *account_debits* listant uniquement les opérations de type DEBIT, avec :
|
||||||
|
|
||||||
|
* date
|
||||||
|
* compte
|
||||||
|
* montant négatif
|
||||||
|
|
||||||
|
Créer une seconde vue *account_credits* (montants positifs).
|
||||||
|
|
||||||
|
## 14. Vue : liste des entreprises avec ancienneté
|
||||||
|
|
||||||
|
Créer une vue *company_age* indiquant :
|
||||||
|
|
||||||
|
* id
|
||||||
|
* name
|
||||||
|
* registration_number
|
||||||
|
* age de l’entreprise en années (`age(current_date, created_at)`)
|
||||||
|
|
||||||
|
## 15. Vue : recapitulatif bancaire complet
|
||||||
|
|
||||||
|
Créer une vue *bank_overview* qui croise :
|
||||||
|
|
||||||
|
* les titulaires
|
||||||
|
* leurs comptes
|
||||||
|
* leurs transactions
|
||||||
|
* leurs opérations
|
||||||
|
|
||||||
|
Voir les adresses des serveurs [postgreSQL](https://sources.neotech.fr/Universite/tp/src/branch/main/geii3_2025.md)
|
||||||
|
|||||||
337
banque.md
337
banque.md
@@ -32,341 +32,4 @@ Pour les entités vous utiliserez le singuler et écrirez le tout en minuscule.
|
|||||||
- [Les vues](banque.vues.md)
|
- [Les vues](banque.vues.md)
|
||||||
- [Les fonctions](banque.functions.md)
|
- [Les fonctions](banque.functions.md)
|
||||||
|
|
||||||
Voir les adresses des serveurs [postgreSQL](https://sources.neotech.fr/Universite/tp/src/branch/main/geii3_2025.md)
|
|
||||||
|
|
||||||
Voir la syntaxe de [postgreSQL](syntaxe.md)
|
Voir la syntaxe de [postgreSQL](syntaxe.md)
|
||||||
|
|
||||||
```sql
|
|
||||||
insert into holder (type) values ('BANK')
|
|
||||||
returning holder;
|
|
||||||
|
|
||||||
insert into bank (id, name) values (1, 'Banque de l''Est');
|
|
||||||
```
|
|
||||||
|
|
||||||
La création d'un titulaire s'effectue en deux étapes :
|
|
||||||
|
|
||||||
- Créer un compte individuel pour _Françoise Zanetti_, née le 12 avril 1995.
|
|
||||||
- Créer une entreprise nommée _Boulangerie de Valorgue_, créée le 19 août 2014, numéro d’immatriculation FR19803269968.
|
|
||||||
- Ajouter un nouveau titulaire : _Justin Hébrard_ né le 11/03/1993.
|
|
||||||
|
|
||||||
|
|
||||||
#### 1.1 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`.
|
|
||||||
- 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é.
|
|
||||||
- Le type doit être contraint à `'PERSON'` ou `'COMPANY'`.
|
|
||||||
|
|
||||||
Il existe deux méthodes pour gérer le type.
|
|
||||||
|
|
||||||
1. vérifier par la commande `CHECK` la validité de la valeur.
|
|
||||||
|
|
||||||
2. utiliser une énumération `enum`.
|
|
||||||
|
|
||||||
|
|
||||||
#### 1.2 Vérifications
|
|
||||||
|
|
||||||
- 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.4 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 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
begin;
|
|
||||||
...
|
|
||||||
commit;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 1.5 Procédure stockée
|
|
||||||
|
|
||||||
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**.
|
|
||||||
|
|
||||||
Créez une **procédure stockée** appelée `create_person_holder`.
|
|
||||||
|
|
||||||
Cette procédure prend en paramètre :
|
|
||||||
|
|
||||||
* le prénom (`p_firstname text`)
|
|
||||||
* le nom (`p_lastname text`)
|
|
||||||
* la date de naissance (`p_birthdate date`)
|
|
||||||
|
|
||||||
Cette procédure doit :
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
3. Si la personne a moins de 15 ans :
|
|
||||||
|
|
||||||
* La procédure doit **refuser la création** et afficher une erreur claire.
|
|
||||||
|
|
||||||
|
|
||||||
>[!NOTE]
|
|
||||||
> La procédure garantit l’**atomicité** : soit tout est créé, soit rien.
|
|
||||||
|
|
||||||
### 2. Les comptes
|
|
||||||
|
|
||||||
- Le solde des comptes ne peuvent être négatifs.
|
|
||||||
- Créez un compte joint à 50/50 pour Françoise et Justin.
|
|
||||||
|
|
||||||
Écrire une requête pour vérifier la somme des parts
|
|
||||||
|
|
||||||
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 :
|
|
||||||
|
|
||||||
* prend en paramètre :
|
|
||||||
|
|
||||||
* `p_holders int[]` (tableau des identifiants de titulaires),
|
|
||||||
* `p_shares numeric[]` (tableau des parts correspondantes).
|
|
||||||
|
|
||||||
* vérifie que :
|
|
||||||
|
|
||||||
* 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 :
|
|
||||||
|
|
||||||
* crée un nouveau compte dans `account` ;
|
|
||||||
* insère les lignes correspondantes dans `account_holder`.
|
|
||||||
|
|
||||||
3. Si une condition échoue :
|
|
||||||
|
|
||||||
* la procédure doit lever une **erreur explicite** (`RAISE EXCEPTION`) ;
|
|
||||||
* aucune insertion ne doit être faite (transaction annulée).
|
|
||||||
|
|
||||||
* Les tableaux peuvent être parcourus avec une boucle :
|
|
||||||
|
|
||||||
```sql
|
|
||||||
for i in 1..array_length(p_holders, 1) loop
|
|
||||||
...
|
|
||||||
end loop;
|
|
||||||
```
|
|
||||||
|
|
||||||
* Pour vérifier la somme :
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select sum(unnest(p_shares));
|
|
||||||
```
|
|
||||||
|
|
||||||
(ou additionner dans la boucle)
|
|
||||||
|
|
||||||
* Vous pouvez lever une erreur personnalisée :
|
|
||||||
|
|
||||||
```sql
|
|
||||||
raise exception 'La somme des parts doit être égale à 1 (%.4f)', somme;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Exemples d’appel
|
|
||||||
|
|
||||||
```sql
|
|
||||||
call create_account(
|
|
||||||
'FR761234567890',
|
|
||||||
'Compte commun',
|
|
||||||
array[1, 5],
|
|
||||||
array[0.5, 0.5]
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Crée un compte partagé 50/50 entre les titulaires 1 et 2.
|
|
||||||
|
|
||||||
|
|
||||||
```sql
|
|
||||||
call create_account(
|
|
||||||
'FR009999999999',
|
|
||||||
'Compte déséquilibré',
|
|
||||||
array[1, 5],
|
|
||||||
array[0.7, 0.4]
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Doit refuser la création avec une erreur claire :
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: La somme des parts (1.1000) doit être égale à 1.0000
|
|
||||||
```
|
|
||||||
### 3. Monnaies et taux de change
|
|
||||||
|
|
||||||
|
|
||||||
```sql
|
|
||||||
insert into currency values ('EUR');
|
|
||||||
insert into currency values ('YEN');
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
insert into exchange_rate values
|
|
||||||
('YEN', '2025-11-03', 177.57),
|
|
||||||
('YEN', '2025-11-04', 176.39),
|
|
||||||
('YEN', '2025-11-05', 176.67),
|
|
||||||
('YEN', '2025-11-06', 177.15),
|
|
||||||
('YEN', '2025-11-07', 176.99);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Séance 3 : Exploitation des données
|
|
||||||
|
|
||||||
[Utiliser la correction](banque.correction.sql) de la base de données
|
|
||||||
|
|
||||||
|
|
||||||
## 1. Vue : taux de change de la veille
|
|
||||||
|
|
||||||
Créer une vue *yesterday_exchange_rates* qui affiche pour **chaque devise** son taux de change de la veille présent dans `exchange_rate`.
|
|
||||||
|
|
||||||
Si aucun taux du jour n’existe, la ligne ne doit pas apparaître.
|
|
||||||
|
|
||||||
|
|
||||||
## 2. Fonction : dernier taux connu pour une devise et une date données
|
|
||||||
|
|
||||||
Créer une fonction *latest_exchange_rate* qui donne, pour une devise, son taux de change le plus récent :
|
|
||||||
|
|
||||||
## 3. Vue : liste détaillées des titulaires
|
|
||||||
|
|
||||||
Créer une vue *holder_details* permettant d’afficher **tous les titulaires** (banque, personne ou entreprise) sous une forme unifiée.
|
|
||||||
|
|
||||||
La vue doit contenir :
|
|
||||||
|
|
||||||
* l’identifiant du titulaire
|
|
||||||
* son type
|
|
||||||
* un champ `display_name` calculé ainsi :
|
|
||||||
|
|
||||||
* pour un titulaire de type *PERSON* : *firstname lastname*
|
|
||||||
* pour un titulaire de type *COMPANY* : *name*
|
|
||||||
* pour un titulaire de type *BANK* : *name*
|
|
||||||
|
|
||||||
## 4. Vue : liste des comptes avec devise et solde
|
|
||||||
|
|
||||||
Créer une vue *account_detail* affichant :
|
|
||||||
|
|
||||||
* l’identifiant du compte
|
|
||||||
* la date d’ouverture
|
|
||||||
* la devise
|
|
||||||
* le solde
|
|
||||||
* le nombre de titulaires du compte
|
|
||||||
|
|
||||||
## 5. Vue : comptes par titulaire
|
|
||||||
|
|
||||||
Créer une vue *holder_accounts* permettant de lister les comptes détenus par chaque titulaire, avec :
|
|
||||||
|
|
||||||
* le titulaire (id et type)
|
|
||||||
* le nom du titulaire
|
|
||||||
* l’identifiant du compte
|
|
||||||
* la part détenue (`share`)
|
|
||||||
* le solde total du compte
|
|
||||||
|
|
||||||
La vue doit fusionner les informations venant de **holder, person et company**.
|
|
||||||
|
|
||||||
## 6. Vue : opérations enrichies
|
|
||||||
|
|
||||||
Créer une vue *operation_details* affichant les opérations avec :
|
|
||||||
|
|
||||||
* la date de l’opération
|
|
||||||
* le compte impacté
|
|
||||||
* la direction (DEBIT ou CREDIT)
|
|
||||||
* le montant de l’opération
|
|
||||||
* le montant signé (crédit positif, débit négatif)
|
|
||||||
* le solde du compte **après l’opération** (bonus : fenêtre analytique)
|
|
||||||
|
|
||||||
|
|
||||||
## 7. Vue : solde converti en EUR
|
|
||||||
|
|
||||||
Créer une vue *account_balance_eur* pour afficher :
|
|
||||||
|
|
||||||
* compte
|
|
||||||
* devise d’origine
|
|
||||||
* solde original
|
|
||||||
* taux de change correspondant à la date du jour
|
|
||||||
* solde converti en EUR (solde × taux)
|
|
||||||
|
|
||||||
## 8. Vue : transactions complètes
|
|
||||||
|
|
||||||
Créer une vue *transaction_summary* affichant un regroupement par transaction :
|
|
||||||
|
|
||||||
* id de la transaction
|
|
||||||
* date
|
|
||||||
* montant total de la transaction (somme des opérations)
|
|
||||||
* liste des comptes concernés (optionnel : concaténation)
|
|
||||||
|
|
||||||
## 9. Vue : comptes en découvert imminent
|
|
||||||
|
|
||||||
Créer une vue *accounts_at_risk* listant les comptes dont le solde est inférieur à 50 (dans leur devise), ou qui auraient un solde négatif s'ils effectuaient un débit supplémentaire de 20.
|
|
||||||
|
|
||||||
> Vérification simple : `balance - 20 < 0 OR balance < 50`.
|
|
||||||
|
|
||||||
## 10. Vue : âge des personnes
|
|
||||||
|
|
||||||
Créer une vue *person_age* indiquant :
|
|
||||||
|
|
||||||
* id
|
|
||||||
* nom complet
|
|
||||||
* date de naissance
|
|
||||||
* âge en années (utiliser `age()`)
|
|
||||||
|
|
||||||
## 11. Vue : répartition des parts d’un compte
|
|
||||||
|
|
||||||
Créer une vue *account_shares* affichant :
|
|
||||||
|
|
||||||
* id du compte
|
|
||||||
* nombre de titulaires
|
|
||||||
* somme des parts
|
|
||||||
* une colonne booléenne `is_valid` vérifiant si la somme = 1
|
|
||||||
|
|
||||||
> Objectif : vérifier que les parts des comptes joints sont bien réparties.
|
|
||||||
|
|
||||||
## 12. Vue : solde par titulaire
|
|
||||||
|
|
||||||
Créer une vue *holder_total_balance* indiquant pour chaque titulaire :
|
|
||||||
|
|
||||||
* id
|
|
||||||
* type
|
|
||||||
* nom
|
|
||||||
* somme des soldes de tous ses comptes (pondérée par `share`), calcul :
|
|
||||||
`total = SUM(share × balance)`
|
|
||||||
|
|
||||||
## 13. Vue : opérations d’un compte en sens unique
|
|
||||||
|
|
||||||
Créer une vue *account_debits* listant uniquement les opérations de type DEBIT, avec :
|
|
||||||
|
|
||||||
* date
|
|
||||||
* compte
|
|
||||||
* montant négatif
|
|
||||||
|
|
||||||
Créer une seconde vue *account_credits* (montants positifs).
|
|
||||||
|
|
||||||
## 14. Vue : liste des entreprises avec ancienneté
|
|
||||||
|
|
||||||
Créer une vue *company_age* indiquant :
|
|
||||||
|
|
||||||
* id
|
|
||||||
* name
|
|
||||||
* registration_number
|
|
||||||
* age de l’entreprise en années (`age(current_date, created_at)`)
|
|
||||||
|
|
||||||
## 15. Vue : recapitulatif bancaire complet
|
|
||||||
|
|
||||||
Créer une vue *bank_overview* qui croise :
|
|
||||||
|
|
||||||
* les titulaires
|
|
||||||
* leurs comptes
|
|
||||||
* leurs transactions
|
|
||||||
* leurs opérations
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## 1. Les titulaires (holder)
|
## 1. Les titulaires (holder)
|
||||||
|
|
||||||
|
Une énumération (souvent appelée enum) est un type de données qui permet de définir un ensemble limité et nommé de valeurs possibles.
|
||||||
|
|
||||||
|
Une énumération sert à représenter une liste finie de choix clairement définis.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
create type holder_type
|
create type holder_type
|
||||||
as enum ('BANK', 'PERSON', 'COMPANY');
|
as enum ('BANK', 'PERSON', 'COMPANY');
|
||||||
@@ -70,8 +74,6 @@ create table company (
|
|||||||
|
|
||||||
- `unique` : La valeur doit être unique (si elle est renseignée). Contrairement à une clé primaire la valeur peut être null.
|
- `unique` : La valeur doit être unique (si elle est renseignée). Contrairement à une clé primaire la valeur peut être null.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 2. Les devises
|
## 2. Les devises
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -135,4 +137,4 @@ create table operation (
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Il n'est pas nécessaire d'utiliser une énumération pour contraindre les deux valeurs de direction.
|
Il n'est pas nécessaire d'utiliser une énumération pour contraindre les deux valeurs de direction. Une contrainte check est suffisante.
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
|
# Les vues
|
||||||
|
|
||||||
|
Une vue SQL est un objet de la base de données qui représente le résultat d’une requête SQL enregistrée.
|
||||||
|
Elle se comporte comme une table virtuelle : on peut la consulter avec SELECT comme une table, mais elle ne stocke pas de données.
|
||||||
|
|
||||||
|
La définition de la vue est enregistrée dans la base, et chaque fois qu’on interroge la vue, la base réexécute la requête sous-jacente.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
create or replace view holder_detail as
|
create or replace view holder_detail as
|
||||||
select h.id, h.type,
|
select h.id, h.type,
|
||||||
case
|
case
|
||||||
when type = 'PERSON' then firstname || ' ' || lastname
|
when type = 'PERSON' then firstname || ' ' || lastname
|
||||||
else name
|
when type = 'COMPANY' then c.name
|
||||||
|
when type = 'BANK' then b.name
|
||||||
end as nom,
|
end as nom,
|
||||||
case
|
case
|
||||||
when type = 'PERSON' then age(birthdate)
|
when type = 'PERSON' then age(birthdate)
|
||||||
when type = 'COMPANY' then age(c.created_at)
|
when type = 'COMPANY' then age(c.created_at)
|
||||||
end as age
|
end as age
|
||||||
from holder h
|
from holder h
|
||||||
left join person p on p.id = h.id
|
left join person p on p.id = h.id
|
||||||
left join company c on c.id = h.id;
|
left join company c on c.id = h.id;
|
||||||
|
left join bank b on b.id = h.id;
|
||||||
```
|
```
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
|||||||
Reference in New Issue
Block a user