SQL Creation D Une BDD Et Requetes
SQL Creation D Une BDD Et Requetes
SQL Creation D Une BDD Et Requetes
Dans ce chapitre, nous voyons comment créer et interroger une base de données. Le langage qui s’est
imposé pour cela est le langage SQL (Structured Query Language). La plupart des SGBD actuels utilisent
ce langage, avec éventuellement quelques variantes. Ceux ne l’utilisant pas (ACCESS par exemple) ont
en général une interface de traduction permettant à l’utilisateur d’entrer ses requêtes en SQL.
Le langage SQL est utilisé tout aussi bien pour la création, le rmplissage et l’interrogation de la base. En
ce sens, les 3 instructions principales sont :
• CREATE pour créer une table
• INSERT pour ajouter des données
• SELECT ... FROM ... WHERE ... pour interroger la base (requête)
À ces trois instructions, on peut ajouter ALTER permettant des modifications sur la structure de la base
de donnée, et UPDATE permettant des modifications sur les enregistrements déjà présents (mises à jour).
Nous utiliserons Python pour créer et manipuler les bases de données. Pour cela, nous utilisons la biblio-
thèque sqlite3. Le principe est ensuite de créer une connection sur une BDD avec la fonction connect,
puis de créer un « curseur » sur cette connection. Grâce à ce curseur, on peut ensuite exécuter sur la base
des instructions SQL, grâce à la méthode execute() applicable au curseur créé. On passe en paramètre
de cette méthode l’instruction SQL sous forme d’une chaîne de caractères (par exemple raw string pour
éviter tout problème d’utilisation des guillemets, apostrophes et retours à la ligne).
La connection doit être fermée en fin de programme.
Par exemple, pour créer une table compositeur dans une base de données, l’instruction SQL est :
CREATE TABLE IF NOT EXISTS compositeur (
idcomp varchar(6) NOT NULL,
nom varchar(30) NOT NULL,
prenom varchar(30) NOT NULL,
naissance int(4),
mort int(4),
PRIMARY KEY (idcomp)
);
Si on veut créer cette base dans un SGBD muni d’une interface graphique conviviale, il est possible de
rentrer directement cette instruction telle quelle, voire de façon encore plus simple, suivant l’interface.
Pour créer cette table via Python, on écrit :
import sqlite3
172 CHAPITRE 12. SQL: CRÉATION D’UNE BDD ET REQUÊTES
connection = sqlite3.connect(’musique.db’)
cur = connection.cursor()
connection.commit()
connection.close()
De même, chaque requête doit ensuite être envoyée via l’instruction cur.execute("""requête""").
Le résultat parvient sous forme d’un objet itérable, les objets (sous format tuple) pouvant être énumérés
à l’aide de for. Par exemple, la fonction suivante créer l’affichage des objets de l’itérable cur, à employer
à l’issue d’une requête.
def affiche(curseur):
for L in curseur:
print(L)
Chaque ligne L du résultat de la requête est affiché sous forme d’un tuple.
Un des intérêts de l’utilisation d’un langage de programmation (par exemple Python) pour l’interrogation
des bases de données est de pouvoir facilement utiliser, et donc traiter informatiquement, le résultat de
la requête.
On créer une nouvelle table avec CREATE TABLE. L’option IF NOT EXISTS permet de ne pas retourner
d’erreur au cas où la table existe déjà (dans ce cas, il n’y a pas de nouvelle table créée)
Lorsqu’on crée une table, il faut préciser :
1. son nom
2. pour chacun des attributs : son nom, son format et d’éventuelles contraintes sur lesquelles nous ne
nous étendons pas. La seule que nous mentionnons (en plus des contraintes liées aux clés) est la
contrainte NOT NULL, imposant qu’une valeur soit nécessairement donnée à cet attribut lors d’un
enregitrement d’une donnée.
3. la donnée de la clé primaire, et eventuellement des clés étangères.
Par exemple, pour créer une table oeuvre (la table compositeur étant définie comme plus haut) :
I Création d’une base de données 173
La clé étangère renvoie à la table compositeur. L’identification se fait sur la clé primaire de cette table.
Une autre syntaxe possible pour la déclaration des clés est :
CREATE TABLE IF NOT EXISTS oeuvre (
idoeuvre varchar(10) NOT NULL PRIMARY KEY,
compositeur varchar(6) NOT NULL REFERENCES compositeur,
oeuvre varchar(128) NOT NULL,
datecomp int(4) NOT NULL,
type varchar(20) NOT NULL);
Les formats classiques des attributs sont varchar(n), int(n), decimal(n,m), pour des chaînes de ca-
ractères, des entiers ou des décimaux. Le paramètre n indique le nombre maximal de caractères, et m
indique le nombre de chiffres après la virgule. D’autres types existent (par exemple date). À découvrir
soi-même.
On peut définir des contraintes beaucoup plus précises sur les différents attributs (notamment des
contraintes d’unicité). Ceci dépasse les objectifs de ce chapitre d’introduction.
Il existe également des instructions permettant de modifier des tables existantes, en particulier ALTER
TABLE. On peut à l’aide de cette instruction modifier le nom d’un attribut, les contraintes, ajouter ou
enlever des clés... Là encore, cela va au-delà des objectifs de ce chapitre.
On indique donc le nom de la table, les attributs qu’on souhaite remplir, dans un ordre déterminé, et
les n-uplets que l’on souhaite définir, séparés par une virgule. On termine la liste par un point-virgule.
Comme le montre la dernière ligne entrée, certains attributs peuvent rester non définis, ce qui revient à
leur attribuer la valeur NULL. Ceci est possible si l’attribut n’a pas été défini avec la contrainte NOT NULL
lors de la création de la table.
En n’indiquant qu’un sous-ensemble des attributs de la table, on peut ne remplir que quelques colonnes.
Les autres seront alors complétées par la valeur NULL :
INSERT INTO compositeur (idcomp, nom, prenom) VALUES
(’RAC 1’, ’Rachmaninov’, ’Serge’),
(’DVO 1’, ’Dvorak’, ’Antonin’);
On peut aussi modifier des données déjà rentrées avec UPDATE ... SET ... :
174 CHAPITRE 12. SQL : CRÉATION D’UNE BDD ET REQUÊTES
UPDATE compositeur SET naissance = 1873, mort = 1943 WHERE idcomp = ’RAC 1’
Il est important de noter que la syntaxe utilisée ici est celle définie dans la bibliothèque
SQLITE de Python, qui s’écarte parfois de la syntaxe standard. De manière générale, d’une
version à l’autre de SQL, il existe souvent des petites variantes dans la syntaxe.
II Interrogation d’une BDD (Requêtes) 175
Ainsi, tout se passe dans une unique table. L’instruction incontournable est :
SELECT Att1, Att2, ...
FROM nom_table
WHERE conditions
Remarque 12.2.2
La sélection SELECT est à voir comme une projection sur un sous-ensemble des colonnes (on ne conserve
que certaines colonnes), alors que la condition WHERE est à voir comme une projection sur un sous-
ensemble de lignes (on ne garde que les lignes vérifiant les conditions requises)
La clause WHERE est optionnelle. Si on ne l’utilise pas, il s’agit de l’extraction des colonnes souhaitées,
sans restriction :
SELECT instrument
FROM instrument
Formellement, il s’agit d’une projection sur l’ensemble des coordonnées selectionnées. Si le tableau s’ap-
pelle TAB, et les attributs selectionnés Ai1 , . . . , Aik , on notera formellement TAB[Ai1 , . . . , Aik ].
L’instruction suivante :
SELECT instrument
FROM instrument
WHERE famille = ’claviers’
Remarquez que pour obtenir l’ensemble des familles d’instruments, on formule assez logiquement la
requête :
SELECT famille
FROM instrument
(’cordes pincées’,)
(’cordes frottées’,)
(’cordes frottées’,)
(’claviers’,)
(’claviers’,)
(’claviers’,)
(’bois’,)
(’bois’,)
(’voix’,)
(’voix’,)
(’bois’,)
(’cuivre’,)
(’bois’,)
(’voix’,)
Ainsi, SELECT ne supprime pas les redondances dans les réponses. On peut le forcer à le faire en ajoutant
l’option DISTINCT :
--Requête:
SELECT DISTINCT famille
FROM instrument
--Réponse:
(’cordes pincées’,)
(’cordes frottées’,)
(’claviers’,)
(’bois’,)
(’voix’,)
(’cuivre’,)
Enfin, on peut remplacer la liste des attributs par * si on veut selectionner l’ensemble des attributs de la
table :
Voici une liste non exhaustive de tests possibles pour exprimer les conditions :
= > < <> <= >= -- comparaisons sur des nombres ou chaînes de caractères.
-- Attention, ’Z’<’a’
IS NULL -- teste si la valeur n’a pas été attribuée
IN (A1,A2,...) -- teste l’appartenance à une liste
BETWEEN a AND b -- appartenance à un intervalle (nombre ou chaîne)
II Interrogation d’une BDD (Requêtes) 177
Par ailleurs, on peut utiliser des opérations sur les attributs, soit pour exprimer des tests, soit pour
former de nouvelles colonnes. Former de nouvelles colonnes se fait en indiquant dans la clause SELECT
les opérations à faire à partir des autres attributs pour créer cette colonne.
SELECT nom
FROM compositeur
WHERE LENGTH(nom) = 6
SELECT UPPER(SUBSTR(nom,1,3))
FROM compositeur
WHERE naissance < 1600
Certaines opérations mathématiques portant sur l’ensemble des valeurs d’un attribut sont possibles (fonc-
tions agrégatives) :
-- FONCTIONS AGRÉGATIVES
Les fonctions agréatives servent à définir de nouvelles colonnes, mais ne peuvent pas être utilisées dans
une condition. Un exemple :
Les fonctions agrégatives peuvent s’utiliser avec l’instruction GROUP BY Att HAVING Cond permettant de
calculer les fonctions agrégatives sur des paquets de données prenant la même valeur pour l’attribut Att,
en ne gardant que les lignes vérifiant la condition Cond.
Remarque 12.2.3
• L’instruction HAVING effectue une selection sur la table obtenue après calcul de la fonction
agrégative.
• On peut aussi utiliser l’instruction WHERE, se plaçant alors avant GROUP BY. Ceci permet d’effec-
tuer une selection avant calcul de la fonction agrégative.
• Attention, les champs sélectionnés doivent en théorie respecter le groupement dans le sens où
la valeur du champ doit être la même pour toutes les entrées groupées dans une part. Dans
notre exemple, c’est le cas puisqu’on selectionne un champ groupé compositeur. Toute autre
sélection est incorrecte en SQL strict, mais tolérée dans certaines variantes (SQLite utilisé sous
Python par exemple). On peut parfois s’en sortir, notamment pour le maximum, en utilisant
une structure ORDER BY ... LIMIT ... explicitée ci-dessous.
Dans notre exemple, la selection peut s’effectuer avant ou après calcul de la fonction agrégative (puisque
la selection se fait sur des paquets d’enregistrement de même compositeur et même type). Ainsi, on aurait
pu écrire :
Dans le cas où la selection peut se faire avant comme ici, c’est préférable, car plus économe en calcul (la
fonction agrégative n’est calculée que sur la selection faite)
Certaines selections ne peuvent être effectuées qu’avant (lorsqu’elles ne sont pas faites sur des paquets
respectant le groupement). Par exemple, extraire de la base la moyenne d’âge des compositeurs nés entre
1800 et 1900 (ici, il n’y a pas de groupement à faire, mais on pourrait imaginer par exemple un groupement
par nationalité, en cherchant cette information dans une autre table, avec la syntaxe qu’on verra plus
loin) :
Certaines selections ne peuvent être effectuées qu’après, notamment celles qui utilisent le résultat du
calcul. Pour accéder à la valeur de la fonction agrégative pour pouvoir l’utiliser dans un test, il faut
au passage renommer la colonne correspondante, ce qui se fait avec AS. Par exemple, pour récupérer la
première sonate des compositeurs avant N dans l’ordre alphabétique, mais seulement si celle-ci est écrite
après 1800, on peut écrire :
En effet, la colonne dt n’a pas encore été calculée au moment du test qui l’utilise.
On aurait pu penser écrire :
Exercice 20
Comprendre la différence entre cette requête et la précédente.
Enfin, notons qu’il est possible d’ordonner les résultats par ordre croissant (numérique ou alphabétique)
grâce à ORDER BY :
La structure ORDER BY peut s’utiliser avec une clause LIMIT n où n est un entier. Dans ce cas, on récupère
uniquement les n premiers résultats après classement. Dans l’exemple ci-dessus, on peut modifier la
requête de sorte à récupérer les 2 premières lignes uniquement :
Cette structure peut être intéressant pour obtenir une ligne réalisant le maximum d’un de ses attributs
(avec LIMIT 1). Mais cela se retourne qu’une des éventuelles plusieurs lignes réalisant le maximum de cet
attribut.
II.2 Sous-requêtes
Ici, B1 est la clé primaire de T1 , donc l’attribut vers lequel réfère la clé étrangère (A1 , T1 ). On exprime
dons la condition sur B1 , en extrayant de T1 l’attribut B1 , lorsque la condition requise est satisfaite. On
teste ensuite l’appartenance de A1 à la liste des admissibles ainsi obtenue.
Remarque 12.2.5
• Il est important de remarquer que le résultat d’une requête SELECT a le même format qu’une
table, et peut donc être réutilisé dans une autre requête SELECT comme toute table. On peut
donc imbriquer des instructions SELECT les unes dans les autres.
• On peut aussi remonter les clés étrangères : dans ce cas, la clé étrangère est (B1 , T ), et A1 est
la clé primaire de T .
Un exemple :
-- Les compositeurs de la base ayant écrit au moins une musique de film:
SELECT nom, prenom
FROM compositeur
WHERE idcomp in (SELECT DISTINCT compositeur
FROM oeuvre
WHERE type = ’film’)
Exercice 21
Extraire de la base les oeuvres (compositeur, titre) utilisant un hautbois solo.
On peut enchaîner de la sorte des sous-requêtes en suivant les clés étrangères, si la condition porte sur
une table accessible en plusieurs étapes.
Exercice 22
Trouver les oeuvres de compositeur français utilisant un instrument soliste dans la famille des cordes
frottées
II Interrogation d’une BDD (Requêtes) 181
Le principe de la sous-requête permet aussi d’utiliser le résultats de fonctions agrégatives dans un test :
-- Compositeurs morts 20 ans avant l’âge moyen des compositeurs de la
-- base
SELECT nom, prenom, naissance, mort
FROM compositeur
WHERE mort - naissance +20 < (SELECT AVG(mort-naissance)
FROM compositeur)
Une intersection peut souvent se reexprimer plus simplement avec une sous-requête et une opération
booléenne AND. Cela peut être efficace lorsqu’on veut croiser deux tables complètes (par exemple deux
tables de clients de deux filiales) :
SELECT * FROM table1
INTERSECT
SELECT * FROM table2
Ici encore, il est souvent possible de remplacer avantageusement une union par une opération booléenne
OR.
On peut aussi faire des exceptions A \ B, sur des tables de même type :
SELECT instrument FROM instrument
WHERE famille = ’bois’
EXCEPT
SELECT instrument FROM soliste
WHERE oeuvre in
(SELECT idoeuvre FROM oeuvre WHERE compositeur IN
(SELECT idcomp FROM compositeur
WHERE idcomp in
(SELECT compositeur FROM nationalité
WHERE pays = ’Allemagne’)))
Exercice 23
Que fait la requête ci-dessus ?
Le produit cartésien se fait en extrayant simultanément les colonnes des deux tables :
SELECT * FROM tab1, tab2
On peut faire le produit cartésien d’extractions de deux tables. Dans ce cas, on liste les attributs à garder
après SELECT, en précisant dans quelle table l’attribut se trouve par une notation suffixe. Si l’attribut
(sous le même nom) n’apparaît pas dans les deux tables, on peut omettre la notation suffixe (il n’y a pas
d’ambiguïté) :
SELECT tab1.Att1, Att2, tab2.Att3 FROM tab1, tab2
Dans cet exemple, l’attribut Att2 est supposé n’exister que dans l’une des deux tables.
Pour éviter des lourdeurs d’écriture (dans les conditions), et pouvoir référer plus simplement aux différents
attributs (c’est utile aussi lorsqu’un attribut est obtenu par un calcul et non directement), on peut donner
un alias aux attributs selectionnés
SELECT tab1.Att1 AS B1, Att2*Att4 AS B2 , tab2.Att3 AS B3 FROM tab1, tab2
Ici, on suppose que Att2 et Att4 sont deux attributs numériques. On pourra utiliser les valeurs des
attributs de la nouvelle table via les alias B1, B2, et B3.
Le produit cartésien est une opération coûteuse et peu pertinente en pratique si elle n’est pas utilisée en
parallèle avec une opération de sélection des lignes. En effet, on associe le plus souvent des lignes n’ayant
rien à voir, par exemple une ligne consacrée à une oeuvre de Mozart et une ligne consacrée aux données
personnelles de Stravinsky. Mais cette opération est le point de départ de la notion de jointure.
II.4 Jointure
La technique de la jointure permet de former une table à l’aide d’une selection d’attributs provenant de
deux tables différentes :
II Interrogation d’une BDD (Requêtes) 183
En d’autre terme, une jointure revient à une instruction de sélection sur le produit cartésien. Par exemple,
pour sélectionner des attributs de deux tables en faisant une jointure en identifiant l’attribut Att4 de la
table 2 et l’attribut Att1 de la table 1 :
Un exemple concret :
Une autre syntaxe parfois plus commode (notamment pour itérer le procéder en opérant des jointures
successives) se fait avec INNER JOIN ... ON ... (le terme INNER étant facultatif) :
On peut faire des jointures successives si on veut des attributs de tableaux plus éloignés, ou si on veut
extraire des données de plus de deux tables :
La nouvelle relation obtenue par projection est donc notée à juste titre π(Ai1 ,...,Aik ) (R).
σE (R) = {x ∈ R | E(x)}
R1 ⋊
⋉A=B R2 = π(A1 ,...,An ,B1 ,...,B̂,...,Bn ) (σA=B (R1 × R2 )),
En SQL, la jointure R1 ⋊
⋉A=B R2 s’effectue de la façon suivante :
SELECT * FROM R1 JOIN R2 ON A=B
Exercice 24
Exprimer les résultats de toutes les requêtes de ce chapitre n’utilisant pas de fonctions agrégatives de
façon formelle, à l’aide des opérations de l’algèbre relationnelle.