- L3 MIASHS/Ingémath/METIS
- Université Paris Cité
- Année 2024-2025
- Course Homepage
- Moodle
Un service de vélos partagés permet aux abonnés d’utiliser des vélos mécaniques ou électriques. Chaque vélo porte un numéro. Un vélo entre en service à une date donnée, Il est retiré du service à une date donnée (pas connue à l’avance).
Chaque abonné souscrit un abonnement pour une durée d’un an à une date donnée. Chaque abonnement possède un numéro, et un titulaire qui possède un nom, un prénom, un âge et un sexe. Un abonnement n’est pas renouvelable. Cela n’empêche pas une personne de souscrire un autre abonnement.
Un abonné emprunte un vélo à une bornette à un instant de départ donné. Une fois le trajet effectué, l’abonné verrouille le vélo sur une bornette à l’instant d’arrivée.
Les bornettes sont situées sur des stations. Chaque station contient un nombre \(n\) (qui peut varier d’une station à l’autre, mais est constant pour une station donnée) de bornettes numérotées de \(1\) à \(n\). Les stations sont numérotées, elles ont un nom et une adresse. Chaque station a une longitude et une latitude.
Proposer un diagrammme entité-association (EA) correspondant à cette modélisation.
On attend un dessin selon les conventions du cours, pas une énumération.
- Distinguer entités fortes et faibles
- Distinguer associations fortes et faibles
- Pour chaque entité préciser l’identifiant (éventuellement relatif)
- Préciser les cardinalités pour chaque participation à une association
Entités
Abonné
AbonnéID
(Identifiant)Nom
Prénom
Age
Genre
DébutDate
(de souscription)
Velo
VeloID
(PK)Type
(Mecanique ou Electrique)DebutServiceDate
FinServiceDate
Bornette
(entité faible)BornetteID
(Identifiant relatif)
Station
StationID
(Identifiant)Nom
Longitude
Latitude
Addresse
#bornettes
Associations
Trajet
StartTime
EndTime
Entités participantes
Abonné
0:nVélo
0:nBornette
(rôle : Départ) 0:nBornette
(rôle : Arrivée) 0:n
Station–Bornette
Entités participantes
Bornette
1:1Station
1:n
C’est un lien partie de entre une entité faible (Bornette
) et une entité forte (Station
). Une station comporte plusieurs bornettes.
Lister les contraintes externes
- Un vélo ne peut pas être emprunté simultanément par deux abonnés
- Le nombre de bornettes rattachées à une station ne peut excéder la limite fixée pour la station
- Un vélo ne peut effectuer de trajets qu’entre sa date de début de service et sa date de retrait de service.
- Un abonné ne peut pas effectuer de trajet avant la date de début ou après la date de fin d’abonnement.
- Une bornette ne peut pas être occupée par deux vélos simultanément.
Proposer une traduction en pattes de corbeau du diagramme EA proposé en réponse
à la première question.
Précisez
- une clé primaire pour chaque table,
- les tables dites intermédiaires,
- pour les liens matérialisant les contraintes référentielles, préciser s’ils sont identifiant ou non.
erDiagram TRAJET }o..|| VELO : velo_id TRAJET }o..|| ABONNE : abonne_id TRAJET }o..|| BORNETTE : bornette_depart_id TRAJET }o..|| BORNETTE : bornette_arrivee_id BORNETTE }o--|| STATION : station_id VELO { num velo_id PK date DebutServiceDate date FinServiceDate text type } ABONNE { num abonne_id PK text nom text prenom num age date debut text genre } TRAJET { num trajet_id PK num velo_id FK num abonne_id FK %% date depart_ts %% date arrivee_ts num bornette_depart_id FK num station_depart_id FK num bornette_arrivee_id FK num station_arrivee_id FK } BORNETTE { num station_id PK, FK num bornette_id PK } STATION { num station_id PK text nom text adresse num latitude num longitude num bornettes }
Proposer un mécanisme pour mettre en place les contraintes externes en SQL lorsque c’est possible sans utiliser les gachettes (TRIGGER
).
On écrit ici en SQL, le schéma correspondant,
CREATE TABLE Abonné (
INT PRIMARY KEY,
AbonnéId VARCHAR(100),
Nom VARCHAR(100),
Prénom INT,
Age CHAR(1),
Genre DATE
DébutDate );
CREATE TABLE Velo (
INT PRIMARY KEY,
VéloId Type ENUM('Mecanique', 'Electrique'),
);
CREATE TABLE Station (
INT PRIMARY KEY,
StationID VARCHAR(100),
Nom DECIMAL(9,6),
Longitude DECIMAL(9,6),
Latitude VARCHAR(255),
Adresse INT
`#Bornettes` );
CREATE TABLE Bornette (
INT,
StationID INT,
BorneID PRIMARY KEY (StationID, BorneID),
FOREIGN KEY (StationID) REFERENCES Station.StationID
)
CREATE TABLE Trajet (
INT PRIMARY KEY,
TrajetID TIMESTAMP,
DébutTS TIMESTAMP,
FinTS INT,
AbonnéID INT,
VéloID INT,
BornetteDépartID INT,
BornetteArriveeID INT,
StationDepartID INT,
StationArriveeID FOREIGN KEY (AbonnéID) REFERENCES Abonné(AbonnéID),
FOREIGN KEY (VeloID) REFERENCES Velo(VeloID),
FOREIGN KEY (StationDepartID, BornetteDépartID)
REFERENCES Bornette(StationID, BornetteID),
FOREIGN KEY (StationArriveeID, BornetteArriveeID)
REFERENCES Bornette(StationID, BornetteID)
);
On suppose que le schéma est muni des dépendances fonctionnelles déduites de la question précédente et de celles qui se déduisent des contraintes de clé primaire. On note cet ensemble de dépendances fonctionnelles \(\Sigma\).
Votre schéma est-il en Forme Normale de Boyce-Codd ?
Quelles actions faut-il effectuer sur votre base pour traduire les événements suivants:
- Souscription d’un abonnement.
- Mise en service d’un vélo.
- Retrait de service d’un vélo.
- Décrochage d’un vélo.
- Accrochage d’un vélo.
On n’attend pas du code. Juste une phrase qui décrit l’opération à effectuer.
- Insertion d’un nouveau tuple dans la table
ABONNE
- Insertion d’un nouveau tuple dans la table
VELO
avec une date de retrait de serviceNULL
ou'infinity'::date
- Mise à jour d’un tuple dans la table
VELO
,RetraitServiceDate
est affecté de la date courante - Insertion d’un nouveau tuple dans la table
TRAJET
.FinTS
,BornetteArriveeID
StationArriveeID
sontNULL
- Mise à jour d’un tuple dans la table
TRAJET
(le tuple correspondant au trajet en cours du vélo raccroché), modification deFinTS
,BornetteArriveeID
StationArriveeID
.
On note que l’accrochage d’un vélo, supposera la recherche du trajet en cours dans la table TRAJET
, puis la mise à jour du tuple concerné.
Une partie des requêtes qui suivent sera compliquée à écrire, parce qu’avec cette modélisation, les trajets en cours sont noyés dans la table TRAJET
. Pour les repérer, il faut examiner la vaneur de FinTS
(NULL
ou 'infinity'::timestamp
).
Pour se faciliter la vie, nous allons retoucher la modélisation présentée plus haut, introduire une nouvelle table TRAJET_EN_COURS
.
CREATE TABLE Trajet (
INT PRIMARY KEY,
TrajetID TIMESTAMP,
DébutTS TIMESTAMP,
FinTS INT,
AbonnéID INT,
VéloID INT,
BornetteDépartID INT,
BornetteArriveeID INT,
StationDepartID INT,
StationArriveeID FOREIGN KEY (AbonnéID) REFERENCES Abonné(AbonnéID),
FOREIGN KEY (VeloID) REFERENCES Velo(VeloID),
FOREIGN KEY (StationDepartID, BornetteDépartID)
REFERENCES Bornette(StationID, BornetteID),
FOREIGN KEY (StationArriveeID, BornetteArriveeID)
REFERENCES Bornette(StationID, BornetteID)
);
CREATE TABLE Trajet_en_cours (
INT PRIMARY KEY,
TrajetID TIMESTAMP,
DébutTS INT UNIQUE,
AbonnéID INT UNIQUE,
VéloID INT,
BornetteDépartID INT,
StationDepartID FOREIGN KEY (AbonnéID) REFERENCES Abonné(AbonnéID),
FOREIGN KEY (VeloID) REFERENCES Velo(VeloID),
FOREIGN KEY (StationDepartID, BornetteDépartID)
REFERENCES Bornette(StationID, BornetteID),
);
erDiagram TRAJET }o..|| VELO : velo_id TRAJET }o..|| ABONNE : abonne_id TRAJET }o..|| BORNETTE : bornette_depart_id TRAJET_EN_COURS }o..|| VELO : velo_id TRAJET_EN_COURS }o..|| ABONNE : abonne_id TRAJET_EN_COURS }o..|| BORNETTE : bornette_depart_id TRAJET }o..|| BORNETTE : bornette_arrivee_id BORNETTE }o--|| STATION : station_id VELO { num velo_id PK date DebutServiceDate date FinServiceDate text type } ABONNE { num abonne_id PK text nom text prenom num age date debut text genre } TRAJET { num trajet_id PK num velo_id FK num abonne_id FK time depart_ts time arrivee_ts num bornette_depart_id FK num station_depart_id FK num bornette_arrivee_id FK num station_arrivee_id FK } TRAJET_EN_COURS { num trajet_id PK num velo_id FK num abonne_id FK time depart_ts num bornette_depart_id FK num station_depart_id FK } BORNETTE { num station_id PK, FK num bornette_id PK } STATION { num station_id PK text nom text adresse num latitude num longitude num bornettes }
Ce modification du schéma, facilite la mise en place de certaines contraintes.
- Pour les tuples de la table
TRAJET_EN_COURS
, les contraintes d’exclusions se réduisent à des contraintes d’unicité survelo_id
etabonne_id
.
Dans la suite, vous formulerez les requêtes dans le schéma relationnel défini par votre schéma en pattes de corbeau.
: 1 point par requête
Liste des trajets en cours à un instant donné
Liste des vélos en trajet depuis plus d’une heure.
Nombre de trajets initiés durant chaque heure de la journée pendant le mois de juin 2024.
Liste des velos qui ont participé à un trajet commencé et achevé dans la même station pendant la dernière semaine.
Lister d’éventuels couples de trajets suspects impliquant le même vélo à un même instant.
Liste des stations qui ont été vides ou pleines pendant la semaine écoulée.
Liste des vélos en service qui n’ont pas roulé depuis plus d’un mois.
Pour chaque couple de stations, durée moyenne des trajets entre la station de départ et la station d’arrivée.
Lister pour chaque station le nombre de bornettes occupées à l’instant courant.
Lister les trajets suspects dont le vélo n’est pas en service.
En PostgreSQL, pour définir un intervalle à l’aide de deux dates debut
et fin
, il suffit d’écrire (début, fin)
. L’intervalle ne contient pas la date de fin.
Pour tester l’intersection/le recouvrement de deux intervalles, on utilise l’opérateur OVERLAPS
-24=# SELECT
bd_2023'2025-01-03'::date, '2025-01-10'::date) OVERLAPS
('2025-01-10'::date, '2025-01-15'::date) ;
(overlaps
----------
false
1 row)
(
SELECT
'2025-01-03 20:26:00'::timestamp, '2025-01-03 21:31:01'::timestamp) OVERLAPS
('2025-01-03 20:50:04'::timestamp, '2025-01-03 21:45:00'::timestamp) ;
(overlaps
----------
t1 row) (
Pour spécifier un intervalle semi-infini (dont la fin n’est pas connue), on peut utiliser 'infinity'::timestamp
pour décrire la borne supérieure.
-24=# SELECT
bd_2023'2025-01-03 20:26:00'::timestamp, 'infinity'::timestamp) OVERLAPS
('2025-01-03 21:32:04'::timestamp, '2025-01-03 21:45:00'::timestamp) ;
(overlaps
----------
t1 row) (
En PostgreSQL, current_timestamp
s’évalue à l’instant courant (de type timestamp
avec timezone
).
En PostgreSQL, pour extraire le mois d’un objet dd
de type date
, vous pouvez utiliser EXTRACT(MONTH FROM dd)
. Le résultat est un entier entre 1 et 12, 1 pour janvier, …
=# SELECT
postgrescurrent_timestamp AS instant,
EXTRACT( MONTH FROM current_timestamp::date) AS le_mois ;
instant | le_mois -------------------------------+---------
2025-06-05 20:26:12.556256+02 | 6
Pour définir un intervalle de temps, il suffit de décrire l’intervalle par une chaine de caratères et de convertir le résultat en type interval
-24=# select '2025-06-05 21:10:38.732237+02'::timestamp - '7 days'::interval ;
bd_2023column?
?----------------------------
2025-05-29 21:10:38.732237