Partie A (12 points) — dans l'esprit de la session principale 2025, section Sciences de l'informatique
Partie A : Gestion de la base de données (12 points)
Le service municipal VéloCité de Sfax met des vélos, classiques ou électriques, à la disposition de ses abonnés dans les stations de la ville. Pour gérer les abonnements, les trajets et l'entretien du parc, il utilise la base de données Gestion_VeloCite, dont la représentation graphique est la suivante :
Les champs des tables sont décrits ci-dessous :
| Champ | Description | Type et propriété(s) |
|---|---|---|
| idStation | Identifiant d'une station | Entier |
| nomStation | Nom d'une station | Chaîne de 20 caractères, unique |
| quartier | Quartier où se trouve une station | Chaîne de 30 caractères |
| capacite | Nombre maximal de vélos d'une station | Entier compris entre 5 et 40 |
| codeModele | Code d'un modèle de vélo | Chaîne de 4 caractères |
| libModele | Libellé d'un modèle | Chaîne de 30 caractères, unique |
| typeModele | Type d'un modèle | « Classique » ou « Electrique » |
| tarifMinute | Prix d'une minute de trajet (dinars) | Réel strictement positif |
| idVelo | Identifiant d'un vélo | Entier |
| dateService | Date de mise en service d'un vélo | Date |
| codeFormule | Code d'une formule d'abonnement | Chaîne de 3 caractères |
| libFormule | Libellé d'une formule | Chaîne de 20 caractères, unique |
| prixMensuel | Prix mensuel d'une formule (dinars) | Réel strictement positif |
| numAbonne | Numéro d'un abonné | Entier |
| nomAbonne | Nom et prénom d'un abonné | Chaîne de 40 caractères |
| telAbonne | Téléphone d'un abonné | Chaîne de 8 caractères, unique |
| emailAbonne | Adresse mail d'un abonné | Chaîne de 50 caractères, facultative |
| idTrajet | Identifiant d'un trajet | Entier |
| dateTrajet | Date d'un trajet | Date (sans heure) |
| dureeMin | Durée d'un trajet en minutes | Entier, vide tant que le vélo n'est pas restitué |
| montant | Montant payé pour un trajet (dinars) | Réel, vide tant que le vélo n'est pas restitué |
| dateRep | Date d'une réparation | Date (sans heure) |
| panne | Description de la panne réparée | Chaîne de 50 caractères |
| cout | Coût d'une réparation (dinars) | Réel strictement positif |
Exercice 1 : Interprétation de la base de données (3 points)
- Donner la représentation textuelle de cette base de données en précisant pour chaque table son nom, ses champs, sa clé primaire et sa (ses) clé(s) étrangère(s) s'il y en a.
- Pour chacune des propositions suivantes, répondre par « Vrai » si elle est vérifiée ou par « Faux » dans le cas contraire :
- Un vélo peut être rattaché en même temps à deux stations différentes.
- Deux vélos du même modèle ont toujours le même tarif à la minute.
- Un vélo peut faire l'objet de deux réparations le même jour.
- Un abonné peut souscrire à deux formules d'abonnement en même temps.
- Un abonné peut emprunter le même vélo plusieurs fois dans la même journée.
- Un trajet peut être enregistré avant que son montant ne soit connu.
- Deux abonnés peuvent avoir le même numéro de téléphone.
- Les tables « Modele » et « Velo » sont vides et les stations d'identifiants 1 à 4 existent déjà. On insère dans l'ordre les lignes du document 1, d'abord dans la table « Modele » puis dans la table « Velo ». Le SGBD génère des erreurs au niveau de quelques lignes. Pour chaque erreur, indiquer le numéro de la ligne, le nom de la table, le nom du champ ainsi que la contrainte d'intégrité non respectée (intégrité de table (clé primaire), intégrité référentielle ou intégrité de domaine).
Document 1 — lignes à insérer dans la table « Modele » puis dans la table « Velo » :
| N° ligne | codeModele | libModele | typeModele | tarifMinute |
|---|---|---|---|---|
| 1 | M1 | Urbain 3V | Classique | 0.040 |
| 2 | M5 | E-City | Electrique | 0.100 |
| 3 | M7 | VéloCargo | Cargo | 0.120 |
| 4 | M5 | E-Trek | Electrique | 0.120 |
| 5 | M8 | Urbain 7V | Classique | 0.050 |
| N° ligne | idVelo | dateService | codeModele | idStation |
|---|---|---|---|---|
| 1 | 201 | 2025-03-10 | M1 | 2 |
| 2 | 202 | 2025-03-10 | M7 | 3 |
| 3 | 201 | 2025-04-02 | M5 | 1 |
| 4 | 204 | 2025-04-02 | M8 | 4 |
Exercice 2 : Interprétation des requêtes (1,5 point)
Soit la liste des rôles suivante :
- a. Supprimer les stations dont le nom contient « Gare ».
- b. Ajouter une contrainte de domaine sur un champ de la table « Formule ».
- c. Afficher les vélos du modèle dont le code est « E-City ».
- d. Modifier la formule d'un abonné dont le numéro est spécifié.
- e. Supprimer les stations dont le nom commence par « Gare ».
- f. Ajouter une nouvelle formule avec des valeurs spécifiques.
- g. Modifier la valeur par défaut d'un champ de la table « Station ».
- h. Afficher les vélos du modèle dont le libellé est « E-City ».
- i. Modifier le prix mensuel d'une formule donnée.
- j. Modifier le type d'un champ de la table « Station ».
Pour chacune des requêtes R1 à R6 ci-dessous, donner la lettre correspondant à son rôle dans la liste ci-dessus.
-- R1
INSERT INTO Formule
VALUES ('F4', 'Etudiant', 10.000);
-- R2
SELECT idVelo, dateService
FROM Velo V, Modele M
WHERE V.codeModele = M.codeModele
AND libModele = 'E-City';
-- R3
UPDATE Abonne SET codeFormule = 'F3'
WHERE numAbonne = 1002;
-- R4
DELETE FROM Station
WHERE nomStation LIKE 'Gare%';
-- R5
ALTER TABLE Formule
ADD CHECK (prixMensuel > 0);
-- R6
ALTER TABLE Station
MODIFY capacite INT DEFAULT 20;Exercice 3 : Manipulation des données de la base (6 points)
On suppose que la base de données est déjà créée et contient des données. Écrire en SQL les requêtes permettant de répondre aux questions suivantes :
- Quels sont les différents quartiers dans lesquels se trouvent les stations ? On affichera, sans répétition, le champ quartier.
- Quels sont les vélos réparés moins de 365 jours après leur mise en service ? On affichera les champs idVelo, dateService, dateRep et panne.
- Comment réduire de 20 % le tarif à la minute des modèles de type « Classique » ?
- Quels sont les modèles dont au moins un vélo a fait l'objet d'une réparation ? On affichera, sans répétition, les champs libModele et typeModele.
- Quels sont les trajets effectués en 2026 par l'abonné nommé « Karim Jmal » ? On affichera les champs idTrajet, dateTrajet et dureeMin, du trajet le plus long au plus court.
- Quel est le nombre d'abonnés de chaque formule ? On affichera les champs codeFormule et libFormule ainsi que le nombre d'abonnés.
- Quels sont les vélos dont le coût total des réparations dépasse strictement 100 dinars ? On affichera les champs idVelo et libModele ainsi que ce coût total.
- Quels sont les vélos qui n'ont été empruntés par aucun abonné pendant l'année 2025 ? On affichera les champs idVelo et dateService.
- Quels sont les vélos avec lesquels a été effectué le trajet le plus cher ? On affichera les champs idVelo, libModele et montant.
Exercice 4 : Extension et modification de la structure de la base de données (1,5 point)
Important : à chaque représentation textuelle demandée, indiquer la clé primaire et la (les) clé(s) étrangère(s) de chaque table.
- Le quartier d'une station est saisi en toutes lettres, si bien qu'un même quartier apparaît sous des écritures différentes (« Sfax Jadida », « Sfax El Jadida »…). Pour éliminer cette incohérence, on propose de modifier la structure de la base en respectant les règles de gestion suivantes : R1 : une station se trouve dans un seul quartier ; R2 : un quartier peut compter plusieurs stations. Proposer une représentation textuelle de la table « Station » ainsi que de toute nouvelle table indispensable.
- Pour suivre l'état du parc, le service inspecte régulièrement ses vélos : lors d'une inspection, on vérifie des points de contrôle (freins, pneus, éclairage, batterie…) et, pour chacun, on enregistre un résultat (« Conforme » ou « Non conforme ») et une remarque. On propose de créer les tables PointControle, Inspection et Verification en respectant les règles de gestion suivantes : R3 : une inspection porte sur plusieurs points de contrôle ; R4 : un point de contrôle est vérifié lors de plusieurs inspections ; R5 : une inspection concerne un seul vélo ; R6 : lors d'une inspection, un point de contrôle n'est vérifié qu'une seule fois, avec l'enregistrement de son résultat.
- Les propositions 1 à 4 ci-dessous sont quatre représentations textuelles erronées. Pour chacune, indiquer la règle de gestion (R3, R4, R5 ou R6) non respectée.
- En déduire une représentation textuelle correcte des tables « PointControle », « Inspection » et « Verification ».
- Proposition 1 : PointControle ( codePoint , libPoint ) ; Inspection ( idInsp , dateInsp ) ; Verification ( idInsp# , codePoint# , resultat , remarque , idVelo# )
- Proposition 2 : PointControle ( codePoint , libPoint ) ; Inspection ( idInsp , dateInsp , idVelo# ) ; Verification ( idVerif , idInsp# , codePoint# , resultat , remarque )
- Proposition 3 : PointControle ( codePoint , libPoint ) ; Inspection ( idInsp , dateInsp , idVelo# , codePoint# , resultat ) ; Verification ( idInsp# , remarque )
- Proposition 4 : PointControle ( codePoint , libPoint , idInsp# , resultat ) ; Inspection ( idInsp , dateInsp , idVelo# ) ; Verification ( idInsp# , remarque )
Voir la correction commentéeAprès avoir posé votre démarche
Exercice 1
1. Idée : une table par cadre ; les champs soulignés forment la clé primaire, les champs suivis de # sont les clés étrangères, toujours du côté ∞ d'un lien.
- Formule ( codeFormule , libFormule , prixMensuel )
- Abonne ( numAbonne , nomAbonne , telAbonne , emailAbonne , codeFormule# )
- Station ( idStation , nomStation , quartier , capacite )
- Modele ( codeModele , libModele , typeModele , tarifMinute )
- Velo ( idVelo , dateService , codeModele# , idStation# )
- Trajet ( idTrajet , dateTrajet , dureeMin , montant , idVelo# , numAbonne# )
- Reparation ( idVelo# , dateRep , panne , cout )
Reparation a une clé primaire composée (idVelo, dateRep) dont idVelo est aussi clé étrangère vers Velo : ce champ est souligné et suivi de #.
2. Idée : une clé étrangère simple ne prend qu'une valeur par ligne ; ce que la clé primaire n'interdit pas est permis ; un attribut décrit la table qui le porte ; le tableau de description dit ce qui est unique ou peut rester vide.
| Proposition | Réponse | Justification |
|---|---|---|
| a | Faux | idStation# est une clé étrangère simple de Velo : chaque vélo ne référence qu'une seule station à la fois. |
| b | Vrai | tarifMinute est un champ de Modele et non de Velo : tous les vélos d'un même modèle partagent cette valeur. |
| c | Faux | La clé primaire (idVelo, dateRep) de Reparation, avec une date sans heure, interdit une seconde réparation du même vélo à la même date. |
| d | Faux | codeFormule# est une clé étrangère simple de Abonne : une seule formule par abonné. |
| e | Vrai | La clé de Trajet est idTrajet : rien n'interdit deux trajets du même abonné, avec le même vélo, à la même date. |
| f | Vrai | dureeMin et montant restent vides tant que le vélo n'est pas restitué : le trajet est enregistré dès l'emprunt. |
| g | Faux | telAbonne est unique d'après le tableau de description des champs. |
3. Idée : rejouer les insertions dans l'ordre en tenant à jour le contenu des tables ; une ligne refusée n'est pas enregistrée et ne peut donc pas servir de ligne mère aux insertions suivantes.
| Ligne | Table | Champ | Contrainte non respectée |
|---|---|---|---|
| 3 | Modele | typeModele | Intégrité de domaine : « Cargo » n'est ni « Classique » ni « Electrique » |
| 4 | Modele | codeModele | Intégrité de table (clé primaire) : « M5 » a déjà été inséré à la ligne 2 |
| 2 | Velo | codeModele | Intégrité référentielle : « M7 » n'existe pas dans Modele, sa ligne 3 ayant été refusée |
| 3 | Velo | idVelo | Intégrité de table (clé primaire) : le vélo 201 vient d'être inséré à la ligne 1 |
Les lignes 1, 2 et 5 de Modele et les lignes 1 et 4 de Velo sont enregistrées. La ligne 2 de Velo est un piège : « M7 » figure bien dans le document 1, mais sa ligne a été refusée.
Exercice 2
Idée : lire d'abord la commande (INSERT, SELECT, UPDATE, DELETE, ALTER), puis le détail qui départage deux rôles voisins : la colonne de la condition, le joker de LIKE, la clause ajoutée.
| Requête | Réponse | Justification |
|---|---|---|
| R1 | f | ajout d'une ligne complète dans Formule |
| R2 | h | la condition porte sur libModele, après jointure avec Modele ; « E-City » est un libellé, le rôle c chercherait un code |
| R3 | d | seul le champ codeFormule de l'abonné 1002 change ; le prix des formules n'est pas touché (rôle i) |
| R4 | e | 'Gare%' : le nom commence par « Gare » ; « contient » s'écrirait '%Gare%' (rôle a) |
| R5 | b | ADD CHECK ajoute une contrainte de domaine sur prixMensuel |
| R6 | g | le type INT est conservé, seule la valeur par défaut change ; le rôle j supposerait un nouveau type |
Exercice 3
1. Idée : un même quartier compte plusieurs stations ; sans DISTINCT, « Sfax Jadida » apparaîtrait autant de fois qu'il a de stations.
SELECT DISTINCT quartier
FROM Station;2. Idée : la date de mise en service est dans Velo, celle de la réparation dans Reparation : une jointure, puis DATEDIFF(date1, date2), qui renvoie le nombre de jours écoulés de date2 à date1.
SELECT V.idVelo, dateService, dateRep, panne
FROM Velo V, Reparation R
WHERE V.idVelo = R.idVelo
AND DATEDIFF(dateRep, dateService) < 365;Avec DATEDIFF(dateService, dateRep), le nombre de jours serait négatif et tous les vélos réparés passeraient le filtre : l'ordre des arguments compte.
3. Idée : réduire de 20 % revient à multiplier par 0,8 ; le type est un champ de Modele, aucune jointure n'est utile.
UPDATE Modele
SET tarifMinute = tarifMinute * 0.8
WHERE typeModele = 'Classique';4. Idée : le libellé est dans Modele, les réparations dans Reparation ; on traverse Velo, dont rien n'est affiché. Un modèle réparé plusieurs fois reviendrait sur plusieurs lignes, d'où DISTINCT.
SELECT DISTINCT libModele, typeModele
FROM Modele M, Velo V, Reparation R
WHERE M.codeModele = V.codeModele
AND V.idVelo = R.idVelo;Trois tables dans le FROM, donc deux conditions de jointure (clé étrangère = clé primaire).
5. Idée : le nom est dans Abonne, les trajets dans Trajet ; deux filtres (nom, année), puis un tri décroissant sur la durée.
SELECT idTrajet, dateTrajet, dureeMin
FROM Trajet T, Abonne A
WHERE T.numAbonne = A.numAbonne
AND nomAbonne = 'Karim Jmal'
AND YEAR(dateTrajet) = 2026
ORDER BY dureeMin DESC;6. Idée : « de chaque formule » : un groupe par formule ; les colonnes affichées sans fonction d'agrégat figurent toutes dans le GROUP BY.
SELECT F.codeFormule, libFormule,
COUNT(numAbonne) AS nbAbonnes
FROM Formule F, Abonne A
WHERE F.codeFormule = A.codeFormule
GROUP BY F.codeFormule, libFormule;La jointure ne garde que les couples formule–abonné existants : une formule sans abonné n'apparaît pas.
7. Idée : le coût total est calculé par vélo (GROUP BY) ; « dépasse strictement 100 » porte sur une somme, c'est donc un filtre de groupes : HAVING, pas WHERE.
SELECT V.idVelo, libModele,
SUM(cout) AS coutTotal
FROM Velo V, Modele M, Reparation R
WHERE V.codeModele = M.codeModele
AND V.idVelo = R.idVelo
GROUP BY V.idVelo, libModele
HAVING SUM(cout) > 100;8. Idée : « aucun … pendant l'année 2025 » : la sous-requête construit la liste des vélos empruntés en 2025, puis on garde ceux qui n'y figurent pas.
SELECT idVelo, dateService
FROM Velo
WHERE idVelo NOT IN (
SELECT idVelo FROM Trajet
WHERE YEAR(dateTrajet) = 2025);Un vélo jamais emprunté, par exemple mis en service à la fin de 2025, fait aussi partie du résultat.
9. Idée : « le plus cher » : chaque montant est comparé au maximum calculé par une sous-requête ; MAX ignore les trajets dont le montant est encore vide.
SELECT V.idVelo, libModele, montant
FROM Velo V, Modele M, Trajet T
WHERE V.codeModele = M.codeModele
AND V.idVelo = T.idVelo
AND montant = (SELECT MAX(montant)
FROM Trajet);WHERE montant = MAX(montant) est refusé : une fonction d'agrégat ne peut pas figurer dans WHERE.
Exercice 4
1. Idée : R1 donne une seule valeur de quartier à chaque station, la clé étrangère va donc dans Station ; le nom du quartier n'est plus saisi qu'une fois, dans la nouvelle table.
- Quartier ( codeQuartier , nomQuartier )
- Station ( idStation , nomStation , capacite , codeQuartier# )
2.a Idée : pour chaque proposition, chercher ce que sa clé ou la place d'un champ interdit ou permet, puis confronter le résultat à chaque règle.
| Proposition | Règle non respectée | Justification |
|---|---|---|
| 1 | R5 | idVelo# est dans Verification : les lignes d'une même inspection pourraient citer des vélos différents ; le vélo doit être porté par Inspection |
| 2 | R6 | la clé idVerif change à chaque ligne : le même point de contrôle pourrait être vérifié deux fois lors de la même inspection |
| 3 | R3 | codePoint# et resultat sont dans Inspection : une inspection ne porterait que sur un seul point de contrôle |
| 4 | R4 | idInsp# est dans PointControle : un point de contrôle ne serait vérifié que lors d'une seule inspection |
2.b Idée : R3 et R4 forment une relation « plusieurs à plusieurs » entre Inspection et PointControle : la table d'association Verification a pour clé le couple (idInsp, codePoint), ce qui traduit R6, et porte le résultat ; le vélo reste dans Inspection (R5).
- PointControle ( codePoint , libPoint )
- Inspection ( idInsp , dateInsp , idVelo# )
- Verification ( idInsp# , codePoint# , resultat , remarque )