Introduction à SQL et MySQL
Introduction à SQL et MySQL
Université de Caen-Normandie
Bruno Crémilleux
SQL : Structured Query Language
2/68
Calcul relationnel sur n-uplets
Univers de SQL : c’est l’ensemble des n-uplets des relations de la
BD.
Variable typée : une variable typée par une table prend ses valeurs
dans les lignes de cette table.
Exemple avec la BD “Commandes” (cf. TD et TP) :
CLIENT C crée la “variable client” C qui peut prendre pour valeur un
des n-uplets de la table CLIENT.
Exemple avec le 1er n-uplet de CLIENT :
[Link] = 1 [Link] = ’Goffin’ [Link] = ’Namur’ [Link] = ’B2’
informations à [Link]
4/68
mysql: premiers pas (2/2)
5/68
mysql(1/3)
PRIMARY KEY (RefC) : déclaration d’une clé (identifiant unique pour chaque
n-uplet)
La table est vide après sa création.
9/68
Insertion de n-uplets
Deux possibilités :
via un script SQL (commande INSERT) :
INSERT INTO CLIENT VALUES (1, ’GOFFIN’, ’Namur’, ’B2’) ;
INSERT INTO CLIENT VALUES (2, ’HANSENNE’, ’Poitiers’, ’C1’) ;
...
11/68
Exemple de requête
(en calcul des n-uplets et SQL)
En algèbre relationnelle :
((R1×R2×...×Rn):F)[A1,A2,...,Am]
16/68
Cas particulier : produit cartésien
SELECT *
FROM R1, R2,...,Rm
En algèbre relationnelle :
(C1 × C2):([Link] = [Link] et [Link] < [Link])
[[Link], [Link]]
17/68
Tri de l’affichage : ORDER BY
Références, types et prix des produits commandés en 2006. Le résultat sera
ordonné selon l’ordre croissant des types de produits puis l’ordre décroissant des
prix :
SELECT DISTINCT [Link], [Link], [Link]
FROM PRODUIT P, DETAIL D, COMMANDE COM
WHERE [Link] = [Link]
AND [Link] = [Link]
AND YEAR([Link]) = 2006
-- psql : EXTRACT(YEAR FROM [Link]) = 2006
ORDER BY [Link], [Link] DESC ;
+----------+--------+-------+
| TypeP | Prix | RefP |
+----------+--------+-------+
| Cheville | 220.00 | CH464 |
| Cheville | 120.00 | CH264 |
| Clou | 105.00 | CL45 |
| Clou | 95.00 | CL60 |
| Planche | 185.00 | PL224 |
+----------+--------+-------+
18/68
Tri de l’affichage :
forme générale d’exécution
19/68
Between
Noms des clients qui habitent Toulouse et dont la référence est inférieure à 6
ou comprise entre 11 et 15 :
+--------+
SELECT [Link] | NomC |
FROM CLIENT C +--------+
| GILLET |
WHERE [Link] = ’Toulouse’ | AVRON |
AND (RefC <= 6 | NEUMAN |
OR RefC BETWEEN 11 AND 15) ; +--------+
22/68
Noms et villes des clients qui habitent dans une ville dont le nom
contient au moins deux e :
+-------+-----------+
| NomC | Ville |
SELECT NomC, Ville
+-------+-----------+
FROM CLIENT
| JACOB | Bruxelles |
WHERE Ville LIKE ’%e%e%’
| MONTI | Geneve |
ORDER BY Ville ;
+-------+-----------+
Noms et villes des clients qui habitent dans une ville dont le deuxième
caractère du nom de ville est un a :
+-----------+-------+
| NomC | Ville |
+-----------+-------+
SELECT NomC, Ville
| GOFFIN | Namur |
FROM CLIENT
| FRANCK | Namur |
WHERE Ville LIKE ’_a%’
| VANDERKA | Namur |
ORDER BY Ville ;
| GUILLAUME | Paris |
+-----------+-------+
23/68
Expressions régulières (REGEXP 1)
Filtrer une chaîne selon un motif donné (la chaîne est dans le résultat dès
que le motif est présent, peu importe ce qui est devant ou derrière le motif).
Caractères jokers :
. un caractère
*, +, ? : répétition de ce qui précède
ˆ : ancrage début de chaîne
$ : ancrage fin de chaîne
[...] : ensemble
| : alternative
(...) : atome de plusieurs caractères
1
postgres: utilisez ˜ au lieu de REGEXP
24/68
Noms et villes des clients qui habitent dans une ville dont le nom
contient au moins deux e :
+-------+-----------+
| NomC | Ville |
SELECT NomC, Ville
+-------+-----------+
FROM CLIENT
| JACOB | Bruxelles |
WHERE Ville REGEXP ’e.*e’
| MONTI | Geneve |
ORDER BY Ville ;
+-------+-----------+
(et pas ’.*e.*e.*’ : notez la différence par rapport à LIKE)
Noms et villes des clients qui habitent dans une ville dont le nom se
termine par un e :
+----------+----------+
| NomC | Ville |
+----------+----------+
| MONTI | Geneve |
SELECT NomC, Ville | VANBIST | Lille |
FROM CLIENT | GILLET | Toulouse |
WHERE Ville REGEXP ’e$’ | AVRON | Toulouse |
ORDER BY Ville ; | MERCIER | Toulouse |
| PONCELET | Toulouse |
| NEUMAN | Toulouse |
+----------+----------+ 25/68
Pour chaque ville dont le nom contient la lettre ’e’ ou la lettre ’i’,
les références des clients qui y habitent :
26/68
Calcul arithmétique
Détail des commandes en y incluant le type de chaque produit commandé, son prix unitaire
et son prix total :
SELECT [Link], [Link], [Link], [Link], [Link] AS "Prix unitaire",
[Link]*[Link] AS PrixTotal
FROM DETAIL D, PRODUIT P
WHERE [Link]=[Link]
ORDER BY [Link], PrixTotal DESC ;
+--------+-------+----------+----------+---------------+-----------+
| RefCom | RefP | TypeP | Quantite | Prix unitaire | PrixTotal |
+--------+-------+----------+----------+---------------+-----------+
| 1 | CH464 | Cheville | 25 | 220.00 | 5500.00 |
| 2 | CH262 | Cheville | 60 | 75.00 | 4500.00 |
| 2 | CL60 | Clou | 20 | 95.00 | 1900.00 |
| 3 | CL60 | Clou | 30 | 95.00 | 2850.00 |
| 4 | CH464 | Cheville | 120 | 220.00 | 26400.00 |
| 4 | CL45 | Clou | 20 | 105.00 | 2100.00 |
| 5 | PL224 | Planche | 600 | 185.00 | 111000.00 |
| 5 | CH464 | Cheville | 260 | 220.00 | 57200.00 |
| 5 | CL60 | Clou | 15 | 95.00 | 1425.00 |
| 6 | CL45 | Clou | 3 | 105.00 | 315.00 |
| 7 | CH264 | Cheville | 180 | 120.00 | 21600.00 |
| 7 | PL224 | Planche | 92 | 185.00 | 17020.00 |
| 7 | CL60 | Clou | 70 | 95.00 | 6650.00 |
| 7 | CL45 | Clou | 22 | 105.00 | 2310.00 |
+--------+-------+----------+----------+---------------+-----------+
COUNT : comptage
SUM : somme
AVG : moyenne
MIN : minimum
MAX : maximum
Principe :
le calcul porte sur un ensemble de n-uplets (et non pas sur un seul
n-uplet). Un tel ensemble est une table ou un élément d’une partition
d’une table.
28/68
Fonctions agrégats
+----------+-----------+------------+-----------+-----------+
| COUNT(*) | SUM(Prix) | AVG(Prix) | Min(Prix) | Max(Prix) |
+----------+-----------+------------+-----------+-----------+
| 7 | 1030.00 | 147.142857 | 75.00 | 230.00 |
+----------+-----------+------------+-----------+-----------+
29/68
COUNT : exemples
+----------+--------------+-----------------------+
| COUNT(*) | COUNT(Ville) | COUNT(DISTINCT Ville) |
+----------+--------------+-----------------------+
| 15 | 15 | 7 |
+----------+--------------+-----------------------+
30/68
Partitionnement
Nombre de fois où le produit CL60 a été commandé :
SELECT [Link], COUNT(*)
FROM DETAIL D
WHERE [Link] = ’CL60’ ;
+------+----------+
| RefP | COUNT(*) |
+------+----------+
| CL60 | 4 |
+------+----------+
Nombre de fois où le produit CL45 a été commandé :
SELECT [Link], COUNT(*)
FROM DETAIL D
WHERE [Link] = ’CL45’ ;
+------+----------+
| RefP | COUNT(*) |
+------+----------+
| CL45 | 3 |
+------+----------+
31/68
Partitionnement
Pour chaque produit, nombre de fois où il a été commandé :
une requête pour chaque produit ?
fastidieux. . .
et la liste des produits n’est pas connue, sauf en interrogeant la base.
å utiliser un partitionnement avec GROUP BY
SELECT [Link], COUNT(*)
FROM DETAIL D
GROUP BY [Link] ;
+-------+----------+
| RefP | COUNT(*) |
+-------+----------+
| CH262 | 1 |
| CH264 | 1 |
| CH464 | 3 |
| CL45 | 3 |
| CL60 | 4 |
| PL224 | 2 |
+-------+----------+ 32/68
Partitionnement
33/68
Partitionnement
Pour chaque produit de prix supérieur à 100, nombre de fois où il a
été commandé :
å le COUNT doit porter sur un ensemble de n-uplets qui est
l’ensemble des commandes d’un même produit : partitionnement
avec GROUP BY selon RefP
+-------+----------+
| RefP | COUNT(*) |
SELECT [Link], COUNT(*) +-------+----------+
FROM DETAIL D, PRODUIT P | CH264 | 1 |
WHERE [Link] = [Link] | CH464 | 3 |
AND [Link] > 100 | CL45 | 3 |
GROUP BY [Link] ; | PL224 | 2 |
+-------+----------+
Remarque :
la jointure élimine les produits non commandés (ici PL222).
34/68
Partitionnement
Pour chaque client qui a fait au moins une commande, le montant
total de ses commandes :
å partitionnement selon les clients.
40/68
Requêtes imbriquées : appartenance
IN : signifie ∈
NOT IN : signifie 6∈
L’élément dont l’appartenance est testée (ici RefP) doit être du type
des éléments retournés par le SELECT imbriqué.
41/68
Requêtes imbriquées : appartenance
Références et prix des produits de prix supérieur à 100 et qui ont été
commandés au moins deux fois en 2005 :
SELECT RefP, Prix
FROM PRODUIT
WHERE Prix > 100
+-------+--------+
AND RefP IN | RefP | Prix |
(SELECT RefP +-------+--------+
FROM DETAIL D, COMMANDE COM | CH464 | 220.00 |
WHERE [Link] = [Link] +-------+--------+
AND YEAR(DateCom) = ’2005’
GROUP BY RefP
HAVING COUNT(*) >= 2) ;
L’élément dont l’appartenance est testée (ici RefP) doit être du type des
éléments retournés par le SELECT imbriqué.
La granularité du SELECT interne (groupe de produits) est différente de
celle du SELECT externe (un produit). 42/68
Requêtes imbriquées : appartenance
Références et prix des produits de prix supérieur à 100 et qui ont été
commandés en 2005 :
une requête imbriquée n’est pas nécessaire :
SELECT [Link], [Link] +-------+--------+
FROM PRODUIT P, DETAIL D, COMMANDE COM | RefP | Prix |
WHERE [Link] = [Link] +-------+--------+
| CH464 | 220.00 |
AND [Link] = [Link] | CH464 | 220.00 |
AND [Link] > 100 | CL45 | 105.00 |
AND YEAR([Link]) = ’2005’ ; +-------+--------+
43/68
Requêtes imbriquées : appartenance
La requête ’Références et prix des produits de prix supérieur à 100 et qui ont
été commandés en 2005” peut aussi être écrite avec une requête
imbriquée mais au prix d’une forte dégradation de la lisibilité et de la
déclarativité. L’écriture suivante est donc déconseillée :
44/68
Requêtes imbriquées : appartenance
Pour chaque produit dont le prix est supérieur à 100, références des clients
n’ayant jamais acheté ce produit :
Remarques :
produit cartésien pour générer toutes les paires (produit, client)
le test d’appartenance s’effectue sur une liste d’attributs
en utilisant COMMANDE dans le premier SELECT, on considère uniquement les
clients qui ont commandé au moins une fois.
Pour considérer tous les clients : utiliser CLIENT
45/68
Requêtes imbriquées
Produit le plus cher (1/2)
SELECT MAX(Prix)
FROM PRODUIT ;
RefP n’est pas unique sur la granularité sur laquelle l’agrégat est calculé (ici, la
table PRODUIT)
46/68
Requêtes imbriquées
Produit le plus cher (2/2)
Références et prix des produits de type cheville qui sont moins chers
que le produit référencé PL224 :
SELECT RefP, Prix
FROM PRODUIT
WHERE TypeP = "Cheville"
and Prix < (SELECT Prix
FROM PRODUIT
WHERE RefP = "PL224") ;
48/68
Requêtes imbriquées : calcul de pourcentage
Nombre de produits achetés par client :
SELECT [Link], COUNT(DISTINCT [Link])
FROM DETAIL D, COMMANDE COM
WHERE [Link]=[Link]
GROUP BY [Link] ;
Remarquez le SELECT imbriqué (i.e. (SELECT COUNT(*) FROM PRODUIT) pour calculer
le nombre total de produits.
49/68
Requêtes imbriquées dans le FROM (1/2)
Les deux calculs (sur des granularités différentes) sont effectués par deux requêtes
imbriquées dans le FROM.
SELECT PRODUIT_CLIENT.RefC, (NbProduitsParClient*100)/NbProduitsTotal AS
Pourcentage
FROM (SELECT [Link], COUNT(DISTINCT [Link]) AS NbProduitsParClient
FROM DETAIL D, COMMANDE COM
WHERE [Link] = [Link]
GROUP BY [Link]) PRODUIT_CLIENT,
(SELECT COUNT(*) AS NbProduitsTotal
FROM PRODUIT) NB_PRODUIT
ORDER BY Pourcentage DESC ;
+------+-------------+
| RefC | Pourcentage |
+------+-------------+
| 7 | 57.1429 |
| 9 | 57.1429 |
| 12 | 42.8571 |
| 14 | 28.5714 |
+------+-------------+
50/68
Requêtes imbriquées dans le FROM (2/2)
Ce qui est faux : l’erreur provient du fait que SQL n’a pas de garantie que
NB_PRODUIT.NbProduitsTotal a une valeur unique pour chaque élément du
GROUP BY
51/68
NOT EXISTS
SELECT [Link]
FROM CLIENT C
WHERE NOT EXISTS
(SELECT *
FROM COMMANDE COM
WHERE [Link] = [Link]) ;
52/68
NOT EXISTS
Cette requête (rappel : Références des clients qui n’ont pas passé de
commande ) peut aussi être écrite avec NOT IN
(et cette écriture est certainement plus naturelle)
SELECT NomC
FROM CLIENT
WHERE RefC NOT IN (SELECT RefC
FROM COMMANDE) ;
53/68
NATURAL JOIN : pour la jointure naturelle
Quantité totale de chevilles commandées par des clients de Toulouse :
SELECT SUM(Quantite)
FROM CLIENT, COMMANDE, DETAIL, PRODUIT
WHERE [Link] = [Link]
AND [Link] = [Link]
AND [Link] = [Link]
AND TypeP = ’Cheville’
AND Ville = ’Toulouse’ ;
55/68
NATURAL JOIN : pour la jointure naturelle
Ajout d’une table COMMUNE :
Namur Belgique 99
Poitiers France 86
CREATE TABLE COMMUNE ( Geneve Suisse 99
Commune VARCHAR(20) NOT NULL, Toulouse France 31
Pays VARCHAR(20) NOT NULL, Bruxelles Belgique 99
Paris France 75
Departement INT NOT NULL, Rome Italie 99
PRIMARY KEY (Commune) Londres Grande-Bretagne 99
) ; Lyon France 69
Anvers Belgique 99
Lille France 59
Dans cet exemple, comme il n’y a pas d’attribut commun entre les deux tables,
cela revient à un produit cartésien (ici : 165 n-uplets).
Il n’y a pas d’erreur de syntaxe.
55/68
NATURAL JOIN : pour la jointure naturelle
Pour que cette requête soit correcte, il faut expliciter la jointure entre CLIENT et
COMMUNE :
SELECT [Link], [Link]
FROM CLIENT C, COMMUNE CO
WHERE [Link] = [Link] ;
+-----------+----------+
| NomC | Pays |
+-----------+----------+
| GOFFIN | Belgique |
| HANSENNE | France |
| MONTI | Suisse |
| GILLET | France |
| AVRON | France |
| FERARD | France |
| MERCIER | France |
| TOUSSAINT | France |
| PONCELET | France |
| JACOB | Belgique |
| VANBIST | France |
| NEUMAN | France |
| FRANCK | Belgique |
| VANDERKA | Belgique |
| GUILLAUME | France |
+-----------+----------+
56/68
NATURAL JOIN : pour la jointure naturelle
Un nom de commune n’est pas forcément unique : ajout d’un identifiant RefC,
pour une capitale, la valeur de RefC est une dizaine (cf. table COMMUNE2) :
12 Namur Belgique 99
22 Poitiers France 86
CREATE TABLE COMMUNE2( 50 Geneve Suisse 99
RefC INT NOT NULL, 21 Toulouse France 31
Commune VARCHAR(20) NOT NULL, 10 Bruxelles Belgique 99
Pays VARCHAR(20) NOT NULL, 20 Paris France 75
Departement INT NOT NULL, 40 Rome Italie 99
PRIMARY KEY (RefC) 30 Londres Grande-Bretagne 99
) ; 25 Lyon France 69
11 Anvers Belgique 99
23 Lille France 59
57/68
NATURAL JOIN : pour la jointure naturelle
Quel est le résultat de la requête ?
SELECT *
FROM CLIENT NATURAL JOIN COMMUNE2 ;
58/68
NATURAL JOIN : pour la jointure naturelle
Quel est le résultat de la requête ?
SELECT *
FROM CLIENT NATURAL JOIN COMMUNE2 ;
+------+---------+-----------+------+-----------+----------+-------------+
| RefC | NomC | Ville | CAT | Commune | Pays | Departement |
+------+---------+-----------+------+-----------+----------+-------------+
| 10 | JACOB | Bruxelles | C2 | Bruxelles | Belgique | 99 |
| 11 | VANBIST | Lille | B1 | Anvers | Belgique | 99 |
| 12 | NEUMAN | Toulouse | C2 | Namur | Belgique | 99 |
+------+---------+-----------+------+-----------+----------+-------------+
RefC est un attribut de même nom pour les 2 tables : NATURAL JOIN le
considère pour la jointure même si les sémantiques de ces deux attributs sont
distinctes. Le résultat pour JACOB est correct uniquement par hasard !
Références des clients qui ont acheté au moins un produit de chaque type de
produits (i.e., ont acheté tous les types de produits).
Écriture de la division selon les opérateurs fondamentaux de l’algèbre
relationnelle : (utilisation d’une double différence) :
Soient R(X1 , ..., Xr , Xr +1 , ..., Xm ) et S(Yr +1 , ..., Ym )
alors R/S = R[X1 , ..., Xr ] − ((R[X1 , ..., Xr ] × S) − R)[X1 , ..., Xr ]
Sur notre exemple :
COM[RefC]-(((COM×P)[[Link],[Link]]) -
((COM ./ D ./ P)[[Link],[Link]]))[RefC]
SELECT [Link]
FROM COMMANDE COM
WHERE [Link] NOT IN
(SELECT [Link]
FROM COMMANDE COM, PRODUIT P
WHERE ([Link], [Link]) NOT IN
(SELECT [Link], [Link]
FROM COMMANDE COM, DETAIL D, PRODUIT P
WHERE [Link] = [Link]
AND [Link] = [Link])) ; 60/68
Division entre ensembles en SQL (2/2)
61/68
Pour obtenir les noms des clients (au lieu de leurs références) :
(écriture avec une requête imbriquée dans un FROM)
SELECT [Link]
FROM (SELECT [Link]
FROM COMMANDE COM
WHERE [Link] NOT IN
(SELECT [Link]
FROM COMMANDE COM, PRODUIT P
WHERE ([Link], [Link]) NOT IN
(SELECT [Link], [Link]
FROM COMMANDE COM, DETAIL D, PRODUIT P
WHERE [Link] = [Link]
AND [Link] = [Link]))) CLIENT_TOUS_TYPEP,
CLIENT C
WHERE CLIENT_TOUS_TYPEP.RefC = [Link] ;
+---------+
| NomC |
+---------+
| NEUMAN |
| MERCIER |
+---------+
62/68
En utilisant CLIENT dans le premier SELECT, il n’est plus nécessaire d’imbriquer
une requête :
SELECT [Link]
FROM CLIENT C, COMMANDE COM
WHERE [Link] = [Link]
AND [Link] NOT IN
(SELECT [Link]
FROM COMMANDE COM, PRODUIT P
WHERE ([Link], [Link]) NOT IN
(SELECT [Link], [Link]
FROM COMMANDE COM, DETAIL D, PRODUIT P
WHERE [Link] = [Link]
AND [Link] = [Link])) ;
+---------+
| NomC |
+---------+
| NEUMAN |
| MERCIER |
+---------+
63/68
Question : dans la requête précédente, pourquoi la jointure avec
COMMANDE du premier SELECT est nécessaire ?
Autrement dit, pourquoi la requête ci-dessous ne produit pas le
résultat demandé ?
SELECT [Link]
FROM CLIENT C
WHERE [Link] NOT IN
(SELECT [Link]
FROM COMMANDE COM, PRODUIT P, CLIENT C
WHERE [Link] = [Link]
AND ([Link], [Link]) NOT IN
(SELECT [Link], [Link]
FROM COMMANDE COM, DETAIL D, PRODUIT P
WHERE [Link] = [Link]
AND [Link] = [Link])) ;
64/68
Autres
66/68