Modèle relationnel et langage SQL
La médiathèque de Nantes enregistre chaque emprunt. Faut-il recopier le nom et l'adresse de l'adhérent à chaque ligne d'emprunt ? Et comment interroger ces données en quelques secondes parmi des millions de lignes ? Le modèle relationnel et le langage SQL répondent à ces deux questions.
Le modèle relationnel
Une base de données relationnelle range l'information dans des tables. Chaque table ressemble à un tableau : les colonnes décrivent des caractéristiques, les lignes décrivent des objets.
| id | nom | prenom | classe |
|---|---|---|---|
| 1 | Martin | Lucas | T1 |
| 2 | Bernard | Emma | T2 |
| 3 | Petit | Léa | T1 |
Pourquoi id plutôt que nom ? Deux élèves peuvent s'appeler Martin. Un numéro attribué à chacun ne peut pas être partagé : il joue le rôle d'une plaque d'immatriculation.
Il faut distinguer la structure (le schéma : noms des attributs et domaines) du contenu (les lignes à un instant donné). Le contenu change tous les jours, la structure change rarement.
Une relation Eleve(id, nom, prenom, classe) a pour clef primaire id. a) Expliquer ce qu'est une relation, un attribut et un domaine, en prenant des exemples dans Eleve.…
Une relation Eleve(id, nom, prenom, classe) a pour clef primaire id.
- a) Expliquer ce qu'est une relation, un attribut et un domaine, en prenant des exemples dans
Eleve.
- b) Expliquer le rôle d'une clef primaire et pourquoi deux élèves ne peuvent pas avoir le même
id.
- c) Donner le schéma de la relation.
Interroger une table : SELECT
Le langage SQL (Structured Query Language) permet de questionner la base. Une requête de lecture a cette forme :
SELECT attributs
FROM table
WHERE condition
ORDER BY attribut;
-
SELECT: les colonnes à afficher (*pour toutes) ; -
FROM: la table interrogée ; -
WHERE: la condition que doivent vérifier les lignes (=,<>,<,>=,AND,OR,NOT) ; -
ORDER BY: le tri (ASCcroissant par défaut,DESCdécroissant) ; -
DISTINCTaprèsSELECT: supprime les doublons.
Les textes s'écrivent entre apostrophes 'Zola'. En SQL, les nombres décimaux s'écrivent avec un point : 7.50.
Prenons Livre(id, titre, auteur, annee, prix) :
| id | titre | auteur | annee | prix |
|---|---|---|---|---|
| 1 | Germinal | Zola | 1885 | 7,50 |
| 2 | Candide | Voltaire | 1759 | 5,00 |
| 3 | Nana | Zola | 1880 | 8,00 |
On considère la table Livre(id, titre, auteur, annee, prix) contenant, entre autres, les livres suivants : (1, "Germinal", "Zola", 1885, 7.50), (2, "Candide",…
On considère la table Livre(id, titre, auteur, annee, prix) contenant, entre autres, les livres suivants : (1, "Germinal", "Zola", 1885, 7.50), (2, "Candide", "Voltaire", 1759, 5.00), (3, "Nana", "Zola", 1880, 8.00).
- a) Écrire la requête SQL qui renvoie les titres des livres de Zola.
- b) Écrire la requête qui renvoie tous les livres triés par prix croissant.
- c) Donner le résultat de la requête
SELECT titre FROM Livre WHERE annee < 1882;.
Modifier les données : INSERT, UPDATE, DELETE
Trois instructions modifient le contenu de la base.
INSERT INTO Livre VALUES (5, 'Thérèse Raquin', 'Zola', 1867, 6.00);
UPDATE Livre SET prix = 6.50 WHERE id = 1;
DELETE FROM Livre WHERE annee < 1800;
-
INSERT INTO … VALUES (…)ajoute une ligne (les valeurs suivent l'ordre des attributs) ; -
UPDATE … SET … WHERE …modifie des valeurs ; -
DELETE FROM … WHERE …supprime des lignes.
Le SGBD (système de gestion de bases de données) refuse d'insérer une ligne dont la clef primaire existe déjà : c'est lui qui garantit l'unicité.
Avec la table Livre(id, titre, auteur, annee, prix) de l'exercice précédent : a) Écrire la requête qui ajoute le livre (4, "Au bonheur des dames", "Zola", 1883, 9.00).…
Avec la table Livre(id, titre, auteur, annee, prix) de l'exercice précédent :
- a) Écrire la requête qui ajoute le livre (4, "Au bonheur des dames", "Zola", 1883, 9.00).
- b) Écrire la requête qui passe le prix du livre d'
id2 à 6,00.
- c) Écrire la requête qui supprime les livres publiés avant 1800.
Agréger : COUNT, SUM, AVG, GROUP BY
Parfois, on ne veut pas les lignes mais un résumé : combien ? quelle somme ? quelle moyenne ? Les fonctions d'agrégation COUNT, SUM, AVG, MIN, MAX résument toutes les lignes sélectionnées en une seule valeur.
SELECT COUNT(*) FROM Livre WHERE auteur = 'Zola';
SELECT AVG(prix) FROM Livre WHERE auteur = 'Zola';
Sur notre table de trois livres, ces requêtes donnent 2, puis 7,75 (la moyenne de 7,50 et 8,00). On peut agréger une expression : SUM(quantite * prix_unitaire).
Pour obtenir un résumé par groupe, on ajoute GROUP BY :
SELECT auteur, COUNT(*) FROM Livre GROUP BY auteur;
Les lignes sont regroupées par valeur de auteur, puis on agrège chaque groupe : on obtient Zola → 2 et Voltaire → 1.
Pour garder seulement certains groupes, on utilise HAVING, qui s'écrit après GROUP BY :
SELECT auteur, COUNT(*) FROM Livre GROUP BY auteur HAVING COUNT(*) >= 2;
Pour ne garder que les premières lignes d'un classement, on trie puis on limite : ORDER BY quantite DESC LIMIT 3 renvoie les trois plus grandes quantités.
La table Vente(id, produit, magasin, quantite, prix_unitaire) recense les ventes d'une chaîne de boulangeries. a) Écrire la requête qui calcule le chiffre d'affaires…
La table Vente(id, produit, magasin, quantite, prix_unitaire) recense les ventes d'une chaîne de boulangeries.
- a) Écrire la requête qui calcule le chiffre d'affaires total ($\text{quantité} \times \text{prix unitaire}$).
- b) Écrire la requête qui donne, pour chaque magasin, la quantité totale vendue.
- c) Écrire la requête qui renvoie le prix unitaire moyen des ventes du produit « baguette ».
- d) Écrire la requête qui renvoie les trois ventes de plus grande quantité.
Relier les tables : clef étrangère et jointure
Pour ne pas recopier les informations d'un adhérent à chaque emprunt, on les range dans une table Adherent et on renvoie vers elle depuis Emprunt.
Avec Adherent (1, Durand, Nantes), (2, Morel, Rezé) et Emprunt (1, 1, « Germinal »), (2, 1, « Nana »), (3, 2, « Candide »), on rassemble les informations avec JOIN :
SELECT Adherent.nom, Emprunt.titre
FROM Adherent
JOIN Emprunt ON Adherent.num = Emprunt.num_adherent;
Le ON indique quelles lignes s'associent : celles dont la clef étrangère est égale à la clef primaire. Résultat : (Durand, Germinal), (Durand, Nana), (Morel, Candide). Le préfixe Table.attribut lève toute ambiguïté.
On combine ensuite jointure et agrégation :
SELECT Adherent.num, COUNT(*)
FROM Adherent JOIN Emprunt ON Adherent.num = Emprunt.num_adherent
GROUP BY Adherent.num;
Ici : 1 → 2 et 2 → 1. Pour relier trois tables, on enchaîne deux jointures : … JOIN B ON … JOIN C ON ….
Pour trouver les lignes qui n'ont aucune correspondance, on utilise une sous-requête avec NOT IN :
SELECT nom FROM Adherent
WHERE num NOT IN (SELECT num_adherent FROM Emprunt);
Cette requête renvoie les adhérents qui n'ont jamais emprunté (aucun ici). Pour compter des valeurs différentes, on écrit COUNT(DISTINCT attribut).
Une médiathèque de Nantes utilise les tables Adherent(num, nom, ville) et Emprunt(id, num_adherent, titre, date_emprunt), où num_adherent est une clef étrangère vers…
Une médiathèque de Nantes utilise les tables Adherent(num, nom, ville) et Emprunt(id, num_adherent, titre, date_emprunt), où num_adherent est une clef étrangère vers Adherent(num).
- a) Expliquer le rôle de la clef étrangère.
- b) Écrire la requête qui renvoie le nom des adhérents et les titres qu'ils ont empruntés.
- c) Écrire la requête qui renvoie le nombre d'emprunts de chaque adhérent (numéro et nombre).
- d) Écrire la requête qui renvoie sans doublon les villes des adhérents ayant emprunté au moins un livre.
Un lycée gère les relations Eleve(id, nom, classe), Matiere(code, intitule) et Note(id_eleve, code_matiere, valeur). La clef primaire de Note est le couple (id_eleve,…
Un lycée gère les relations Eleve(id, nom, classe), Matiere(code, intitule) et Note(id_eleve, code_matiere, valeur). La clef primaire de Note est le couple (id_eleve, code_matiere).
- a) Donner les clefs étrangères du schéma.
- b) Écrire la requête qui renvoie, pour chaque élève (nom), sa moyenne en « Mathématiques ».
- c) Écrire la requête qui renvoie les noms des élèves de classe « T1 » dont la moyenne en « NSI » dépasse 14.
- d) Expliquer pourquoi l'insertion d'une note avec un
id_eleveinexistant doit être refusée et par quel mécanisme.
Soit la table Film(id, titre, realisateur, annee) et la table Projection(id_film, salle, heure) où id_film est une clef étrangère. a) Écrire la requête qui renvoie les…
Soit la table Film(id, titre, realisateur, annee) et la table Projection(id_film, salle, heure) où id_film est une clef étrangère.
- a) Écrire la requête qui renvoie les titres des films projetés dans au moins deux salles différentes.
- b) Écrire la requête qui renvoie les réalisateurs dont aucun film n'est projeté.
- c) Expliquer la différence entre
WHEREetHAVINGen justifiant l'usage nécessaire de l'un d'eux dans la question a).
Redondance, anomalies et décomposition
Reprenons la question du début : peut-on tout mettre dans une seule table ? Regardons Pret(num_pret, nom_adherent, ville_adherent, titre) :
| num_pret | nom_adherent | ville_adherent | titre |
|---|---|---|---|
| 1 | Durand | Nantes | Germinal |
| 2 | Durand | Nantes | Nana |
| 3 | Morel | Rezé | Candide |
La ville de Durand est écrite deux fois : c'est une redondance. Elle provoque trois anomalies :
-
mise à jour : si Durand déménage, il faut modifier toutes ses lignes, sinon la base devient incohérente ;
-
insertion : on ne peut pas enregistrer un nouvel adhérent tant qu'il n'a rien emprunté ;
-
suppression : en supprimant le prêt n° 3, on perd aussi toute trace de Morel.
Ici : Adherent(num, nom, ville) et Pret(num_pret, num_adherent, titre), avec num_adherent clef étrangère vers Adherent. On retrouve la table d'origine par une jointure.
On crée les tables avec CREATE TABLE, en déclarant les clefs :
CREATE TABLE Adherent (
num INTEGER PRIMARY KEY,
nom TEXT,
ville TEXT
);
CREATE TABLE Pret (
num_pret INTEGER PRIMARY KEY,
num_adherent INTEGER,
titre TEXT,
FOREIGN KEY (num_adherent) REFERENCES Adherent(num)
);
Pour une clef primaire composée de deux attributs, on écrit PRIMARY KEY (a, b) en fin de déclaration. Les domaines usuels sont INTEGER, REAL, TEXT.
Voici une table unique Commande(num, client, adresse_client, produit, prix) : (1, "Durand", "12 rue Verte, Rennes", "stylo", 2,00) (2, "Durand", "12 rue Verte, Rennes",…
Voici une table unique Commande(num, client, adresse_client, produit, prix) :
(1, "Durand", "12 rue Verte, Rennes", "stylo", 2,00)
(2, "Durand", "12 rue Verte, Rennes", "cahier", 3,50)
(3, "Morel", "5 av. Foch, Brest", "stylo", 2,00)
- a) Repérer la redondance présente dans cette table.
- b) Décrire une anomalie de mise à jour, une anomalie d'insertion et une anomalie de suppression.
- c) Proposer un schéma à trois relations, avec clefs primaires et étrangères, qui supprime ces anomalies.
On considère la relation Inscription(num_etudiant, nom_etudiant, code_cours, intitule_cours, enseignant), avec la clef primaire (num_etudiant, code_cours). Un cours a un…
On considère la relation Inscription(num_etudiant, nom_etudiant, code_cours, intitule_cours, enseignant), avec la clef primaire (num_etudiant, code_cours). Un cours a un seul intitulé et un seul enseignant.
- a) Montrer que cette relation présente des redondances et des anomalies d'insertion, de suppression et de mise à jour.
- b) Proposer une décomposition en relations sans redondance, en précisant clefs primaires et étrangères.
- c) Écrire en SQL les instructions
CREATE TABLEcorrespondantes.
- d) Écrire la requête qui reconstitue la liste (nom d'étudiant, intitulé du cours, enseignant).
L'essentiel
-
Une relation (table) a des attributs (colonnes) munis d'un domaine ; la clef primaire identifie chaque ligne de façon unique ; une clef étrangère renvoie à la clef primaire d'une autre table.
-
Lecture :
SELECT … FROM … WHERE … ORDER BY …(DISTINCTsupprime les doublons,LIMITgarde les premières lignes). -
Mise à jour :
INSERT INTO,UPDATE … SET … WHERE,DELETE FROM … WHERE: ne jamais oublier leWHERE. -
Agrégation :
COUNT,SUM,AVG,MIN,MAX;GROUP BYregroupe,HAVINGfiltre les groupes (WHEREfiltre les lignes). -
JOIN … ON clef_étrangère = clef_primairerassemble les informations de plusieurs tables. -
Une table qui répète des informations souffre d'anomalies de mise à jour, d'insertion et de suppression : on la décompose, et
CREATE TABLEdéclare clefs primaires et étrangères.