- 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)NomPrénomAgeGenreDébutDate(de souscription)
VeloVeloID(PK)Type(Mecanique ou Electrique)DebutServiceDateFinServiceDate
Bornette(entité faible)BornetteID(Identifiant relatif)
StationStationID(Identifiant)NomLongitudeLatitudeAddresse#bornettes
Associations
Trajet
StartTimeEndTime
Entités participantes
Abonné0:nVélo0:nBornette(rôle : Départ) 0:nBornette(rôle : Arrivée) 0:n
Station–Bornette
Entités participantes
Bornette1:1Station1: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é (
AbonnéId INT PRIMARY KEY,
Nom VARCHAR(100),
Prénom VARCHAR(100),
Age INT,
Genre CHAR(1),
DébutDate DATE
);CREATE TABLE Velo (
VéloId INT PRIMARY KEY,
Type ENUM('Mecanique', 'Electrique'),
);CREATE TABLE Station (
StationID INT PRIMARY KEY,
Nom VARCHAR(100),
Longitude DECIMAL(9,6),
Latitude DECIMAL(9,6),
Adresse VARCHAR(255),
`#Bornettes` INT
);CREATE TABLE Bornette (
StationID INT,
BorneID INT,
PRIMARY KEY (StationID, BorneID),
FOREIGN KEY (StationID) REFERENCES Station.StationID
)CREATE TABLE Trajet (
TrajetID INT PRIMARY KEY,
DébutTS TIMESTAMP,
FinTS TIMESTAMP,
AbonnéID INT,
VéloID INT,
BornetteDépartID INT,
BornetteArriveeID INT,
StationDepartID INT,
StationArriveeID INT,
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
VELOavec une date de retrait de serviceNULLou'infinity'::date - Mise à jour d’un tuple dans la table
VELO,RetraitServiceDateest affecté de la date courante - Insertion d’un nouveau tuple dans la table
TRAJET.FinTS,BornetteArriveeIDStationArriveeIDsontNULL - Mise à jour d’un tuple dans la table
TRAJET(le tuple correspondant au trajet en cours du vélo raccroché), modification deFinTS,BornetteArriveeIDStationArriveeID.
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 (
TrajetID INT PRIMARY KEY,
DébutTS TIMESTAMP,
FinTS TIMESTAMP,
AbonnéID INT,
VéloID INT,
BornetteDépartID INT,
BornetteArriveeID INT,
StationDepartID INT,
StationArriveeID INT,
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 (
TrajetID INT PRIMARY KEY,
DébutTS TIMESTAMP,
AbonnéID INT UNIQUE,
VéloID INT UNIQUE,
BornetteDépartID INT,
StationDepartID INT,
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_idetabonne_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
bd_2023-24=# SELECT
('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
----------
t
(1 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.
bd_2023-24=# SELECT
('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
----------
t
(1 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, …
postgres=# SELECT
current_timestamp AS instant,
EXTRACT( MONTH FROM current_timestamp::date) AS le_mois ;
instant | le_mois
-------------------------------+---------
2025-06-05 20:26:12.556256+02 | 6Pour 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
bd_2023-24=# select '2025-06-05 21:10:38.732237+02'::timestamp - '7 days'::interval ;
?column?
----------------------------
2025-05-29 21:10:38.732237