diff --git a/Reponses.md b/Reponses.md new file mode 100644 index 0000000..81340cb --- /dev/null +++ b/Reponses.md @@ -0,0 +1,5 @@ +SELECT data->>'sku' AS sku, + data->'manufacturer'->>'name' AS marque +FROM products; + +db.commandes.find({ reference: '51943385-B' }) diff --git a/banque.correction.md b/banque.correction.md new file mode 100644 index 0000000..4975b3b --- /dev/null +++ b/banque.correction.md @@ -0,0 +1,114 @@ +# Corrections + +## 1. Les titulaires + +`holder` : table commune à tous les titulaires + +- identifiant unique (`id`) +- type de titulaire (`type` = 'PERSON' ou 'COMPANY') +- date de création + +```sql +create table holder ( + id bigint primary key generated always as identity, + type text not null check (type in ('PERSON', 'COMPANY')), + created_at timestamp not null default now() +); +``` + +`person` : informations propres aux personnes physiques +- prénom, nom, date de naissance + +```sql +create table person ( + id bigint primary key references holder(id) on delete cascade, + firstname text not null, + lastname text not null, + birthdate date not null +); +``` + +`company` : informations propres aux entreprises +- raison sociale, numéro d’immatriculation, date de création + +```sql +create table company ( + id bigint primary key references holder(id) on delete cascade, + name text not null, + registration_number text unique not null, + created_at date not null +); +``` + +### Tests d'insertion des données + +```sql +-- Création d’un titulaire de type personne physique +insert into holder(type) values ('PERSON') returning id; + +-- Utilisons l'id qui est retourné (1) +insert into person(id, firstname, lastname, birthdate) + values (1, 'Françoise', 'Zanetti', '1995-04-12'); +``` + +```sql +-- Création d’un titulaire de type entreprise +insert into holder(type) values ('COMPANY') returning id; + +-- Utilisons l'id qui est retourné (2) +insert into company(id, name, registration_number, created_at) + values (2, 'Boulangerie de Valorgue', 'FR19803269968', '2014-08-19'); +``` + +### Vérification + +```sql +select h.id, h.type, h.created_at, + p.firstname || ' ' || p.lastname as person, + c.name as company +from holder h +left join person p on p.id = h.id +left join company c on c.id = h.id +order by h.id; +``` + +#### 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 + +Contrainte déclarative + +```sql +alter table person +add constraint chk_person_minimum_age +check (birthdate <= current_date - interval '15 years'); +``` + +Contrainte procédurale avec un trigger : + +```sql +create or replace function check_person_age() +returns trigger as $$ +begin + if new.birthdate > current_date - interval '15 years' then + raise exception 'Holder must be at least 15 years old'; + end if; + return new; +end; +$$ language plpgsql; + +create trigger trg_check_person_age +before insert or update on person +for each row execute procedure check_person_age(); +``` diff --git a/banque.md b/banque.md index f4a22a7..4864dfa 100644 --- a/banque.md +++ b/banque.md @@ -1,7 +1,64 @@ -# Travaux pratiques +# Modélisation d'un système bancaire -L’objectif est de modéliser et manipuler la base de données d’un système bancaire simplifié, dans lequel : +## Objectifs -- Un compte bancaire (_account_) appartient à un ou plusieurs titulaires (_holders_). -- Un titulaire peut être un individu (_person_) ou une société (_company_). +- 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. + +## Contexte + +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 : + +1. Les clients de la banque, appelés titulaires (_holders_), +2. Les comptes bancaires (_accounts_), +3. Le lien entre les titulaires et les comptes. + +## 1. Les titulaires + +Un titulaire (_holder_) peut être une personne physique (_person_) ou une entreprise (_company_). + +### 1.1 Exemple de données + +- 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. + +### 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 + +Lister tous les titulaires. + +Supprimer un titulaire, vérifier que cela supprime l'individu ou la société correspondante. + +### 1.5 Réflexion + +- 1. Pourquoi séparer `person` et `company` ? +- 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 + +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. + +## 2. Les comptes + +- Un compte bancaire appartient à un ou plusieurs titulaires (_holders_). - Chaque compte dispose d’un numéro de compte (_account number_) unique et d’un solde. diff --git a/postgresql-entrypoint-initdb.d/01_initdb.sql b/postgresql-entrypoint-initdb.d/01_initdb.sql index 812282e..7f4c8a7 100644 --- a/postgresql-entrypoint-initdb.d/01_initdb.sql +++ b/postgresql-entrypoint-initdb.d/01_initdb.sql @@ -469,10 +469,10 @@ END $$; DROP table exchange; -- ---------------------------------------------------------------------- --- Titulaires +-- Titulaires (Holders) -- ---------------------------------------------------------------------- -CREATE TABLE bank.titulaire ( +CREATE TABLE bank.holder ( id bigint primary key generated always as identity, type_titulaire TEXT CHECK (type_titulaire IN ('individu', 'société')) NOT NULL, created_at timestamp with time zone not null default now() @@ -484,7 +484,7 @@ DECLARE new_titulaire_id INTEGER; BEGIN IF NEW.id IS NULL THEN - INSERT INTO bank.titulaire (type_titulaire) VALUES ('individu') + INSERT INTO bank.holder (type_titulaire) VALUES ('individu') RETURNING id INTO new_titulaire_id; NEW.id := new_titulaire_id; END IF; @@ -503,7 +503,7 @@ DECLARE new_titulaire_id INTEGER; BEGIN IF NEW.id IS NULL THEN - INSERT INTO bank.titulaire (type_titulaire) VALUES ('société') + INSERT INTO bank.holder (type_titulaire) VALUES ('société') RETURNING id INTO new_titulaire_id; NEW.id := new_titulaire_id; END IF; @@ -530,7 +530,7 @@ create table bank.account ( create table bank.account_holders ( account_id bigint NOT NULL REFERENCES bank.account(id) ON DELETE CASCADE, - titulaire_id int NOT NULL REFERENCES bank.titulaire(id) ON DELETE CASCADE, + titulaire_id int NOT NULL REFERENCES bank.holder(id) ON DELETE CASCADE, share numeric(5,2) CHECK (share >= 0 AND share <= 100), role text DEFAULT 'Titulaire', PRIMARY KEY (account_id, titulaire_id)