Vous êtes sur la page 1sur 6

Fabien Coelho Fabien Coelho SQL bien avancé

SQL bien avancé

inspiré en partie par :

SQL Hacks
Tips & Tools for Digging into Your Data
Andrew Cumming & Gordon Russel
O’Reilly Hacks Series

Fabien Coelho

datacharmer.org MINES ParisTech

Composé avec LATEX, révision numéro 3802

1 2

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Valeurs directes (1)


Approche orientée problème
— construire table constante au vol

1. valeurs directes 7. regroupements partiels — SELECT . . . UNION


jour mois
2. énumérer une série 8. calculer la valeur médiane SELECT 1 AS jour, mois.nom AS mois
1 ...
FROM (SELECT ’janvier’ AS nom
3. requête latérale 9. générer une somme partielle UNION SELECT ’février’ 1 juin

UNION SELECT ’mars’ 1 avril


4. upsert 10. fermeture transitive
UNION SELECT ’avril’ 1 février
5. héritage 11. SQL est-il Turing complet ? UNION SELECT ’mai’
1 janvier
UNION SELECT ’juin’
1 mai
6. aggrégation partielle 12. comparer deux tables UNION SELECT ’...’) AS mois;
1 mars

3 4

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Valeurs directes (2)


Valeurs directes (3)
— VALUES(. . . ), (. . . ). . . justifie le S !
— aussi utilisé pour INSERT — pas forcément dans une sous requête

jour mois column1 column2

1 juillet 1 janvier
SELECT 1 AS jour, mois.nom AS mois
1 août VALUES (1, ’janvier’), (2, ’février’), 2 février
FROM (VALUES
(’juillet’), (’août’), (’septembre’), 1 septembre (3, ’mars’), (4, ’avril’), 3 mars
(’octobre’), (’novembre’), (’décembre’), (5, ’mai’), (6, ’juin’); 4 avril
1 octobre
(’...’)) 5 mai
1 novembre
AS mois(nom);
1 décembre 6 juin

1 ...

5 6

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Énumérer une série Génération d’une relation. . .

— combien de vendredi 13 au 20ème siècle ? — génération d’intervale avec generate series()


day nb SELECT
lundi 173 CASE EXTRACT(DOW FROM DATE (year || ’-’ || month || ’-13’))
mercredi 173
WHEN 0 THEN ’dimanche’ WHEN 1 THEN ’lundi’ WHEN 2 THEN ’mardi’
WHEN 3 THEN ’mercredi’ WHEN 4 THEN ’jeudi’ WHEN 5 THEN ’vendredi’
samedi 172
WHEN 6 THEN ’samedi’ END AS day,
dimanche 171 COUNT(*) AS nb
vendredi 171 FROM generate series(1901,2000) AS year,
jeudi 171 generate series(1,12) AS month
mardi 169
GROUP BY day
ORDER BY nb DESC;

7 8
Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Requête à côté LATERAL

— dans sous-requête FROM


Tableau constant... — peut utiliser les valeurs des tuples à sa gauche
SELECT — peu utile ? expressions, jointures, aggrégations, fenêtres. . .
-- un tableau constant
i pow
(’{dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi}’::TEXT[])
[1 + EXTRACT(DOW FROM DATE (year || ’-’ || month || ’-13’))] AS day, -- version avec LATERAL 0 1

COUNT(*) AS nb SELECT i, n AS pow 1 2


FROM generate series(1901,2000) AS year, FROM generate series(0, 7) AS i, 2 4
generate series(1,12) AS month LATERAL (SELECT POW(2, i)) AS p2(n); 3 8
GROUP BY day
-- version sans LATERAL 4 16
ORDER BY nb DESC;
SELECT i, POW(2, i) AS pow 5 32
FROM generate series(0, 7) AS i;
6 64

7 128

9 10

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Exemple UPSERT
id name
Données initiales 1 Calvin
MERGE/UPSERT : INSERT ou UPDATE
2 hbs

— insertion ou mise à jour si existe déjà


INSERT INTO Heroes(name) VALUES (’Calvin’)
économise un test et sa latence
ON CONFLICT (name) DO NOTHING;
— différentes syntaxes selon les bases de données. . .
MERGE (standard, . . . ), UPSERT, INSERT ... ON INSERT INTO Heroes VALUES (2, ’Hobbes’)
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name;
— PostgreSQL : INSERT ... ON CONFLICT DO NOTHING
ou INSERT ... ON CONFLICT DO UPDATE ... INSERT INTO Heroes VALUES (3, ’Susie’)
aussi clause WHERE, données initiales EXCLUDED ON CONFLICT (name) DO NOTHING;
id name

1 Calvin
Données finales
2 Hobbes

3 Susie

11 12

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Héritage entre relations

— lien modèles relationnel et objet


Remplissage d’une hiérarchie de tables
CREATE TABLE Identité(
pid SERIAL PRIMARY KEY, INSERT INTO Identité(nom) VALUES
nom TEXT UNIQUE NOT NULL); (’Mum’), (’Dad’);
CREATE TABLE Élève( INSERT INTO Élève(nom, promo) VALUES
promo TEXT NOT NULL, (’Calvin’, ’4th’),
UNIQUE(nom)
(’Hobbes’, ’4th’);
) INHERITS (Identité);
INSERT INTO Professeur(nom, matière) VALUES
CREATE TABLE Professeur(
(’Rosalyn’, ’math’);
matière TEXT NOT NULL,
UNIQUE(nom)
) INHERITS (Identité);

13 14

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Aggrégations partielles FILTER

Extraction sur une hiérarchie de tables — applique une aggrégations sur certains tuples seulement
pid nom — syntaxe : AGG(...) FILTER(WHERE ...)
1 Mum
-- nombre de films avant et après 1950
2 Dad
SELECT * FROM Identité; SELECT
3 Calvin
COUNT(*) FILTER(WHERE année < 1950) AS "avant",
4 Hobbes
COUNT(*) FILTER(WHERE année >= 1950) AS "après",
5 Rosalyn
COUNT(*) AS "total"
pid nom promo
FROM films;
SELECT * FROM Élève; 3 Calvin 4th

4 Hobbes 4th avant après total

5 6 11

15 16
Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

pays langue pop

Allemagne Allemand 81.5


GROUPING SETS, CUBE, ROLLUP
Exemple grouping sets Autriche Allemand 8.7

Belgique Allemand 0.1


— aggrégation sur des sous-ensembles de GROUP BY
CREATE TABLE PaysLanguePopulation( Belgique Français 4.1
GROUPING SETS liste de groupes de colonnes id SERIAL PRIMARY KEY,
Belgique Néerlandais 7.0
pays TEXT NOT NULL,
ROLLUP tous les préfixes de colonnes, y compris vide France Français 66.1
langue TEXT NOT NULL,
pop FLOAT4 NOT NULL, Italie Italien 60.8
CUBE toutes les sous-ensembles de colonnes UNIQUE (pays, langue) Pays-Bas Néerlandais 16.9

— équivalent à UNION, valeurs non gardées remplacées par NULL ); Suisse Allemand 5.2

Suisse Français 1.7

Suisse Italien 0.5

17 18

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

pays langue pop Équivalent avec UNION pays langue pop

Allemagne 81.5 (probablement moins performant) Allemagne 81.5

Autriche 8.7 Autriche 8.7


Cumuls par pays et par langues SELECT pays AS pays, NULL AS langue,
Belgique 11.2 SUM(pop) AS pop Belgique 11.2

France 66.1 FROM PaysLanguePopulation France 66.1


SELECT pays, langue,
Italie 60.8 GROUP BY pays -- premier groupe Italie 60.8
SUM(pop) AS pop
Pays-Bas 16.9
UNION Pays-Bas 16.9
FROM PaysLanguePopulation SELECT NULL, langue, SUM(pop)
Suisse 7.4 Suisse 7.4
GROUP BY GROUPING SETS FROM PaysLanguePopulation
Allemand 95.5 GROUP BY langue -- second groupe Allemand 95.5
((pays), (langue), ())
Français 71.9 UNION Français 71.9
ORDER BY pays, langue; SELECT NULL, NULL, SUM(pop)
Italien 61.3 Italien 61.3

Néerlandais 23.9
FROM PaysLanguePopulation Néerlandais 23.9
-- dernier groupe
252.6 252.6
ORDER BY pays, langue;

19 20

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

pays langue pop


pays langue pop
Allemagne Allemand 81.5
Allemagne Allemand 81.5 Exemple CUBE
Exemple ROLLUP Allemagne 81.5
Autriche Allemand 8.7
Autriche Allemand 8.7
Belgique Allemand 0.1
SELECT pays, langue,
SELECT pays, langue, Autriche 8.7
Suisse Allemand 5.2 SUM(pop) AS pop
SUM(pop) AS pop Belgique Allemand 0.1
Belgique Français 4.1 FROM PaysLanguePopulation
FROM PaysLanguePopulation Belgique Français 4.1
France Français 66.1 GROUP BY CUBE(pays, langue)
GROUP BY ROLLUP(pays, langue) Belgique Néerlandais 7
Suisse Français 1.7 -- équivalent à GROUPING SETS
-- équivalent à GROUPING SETS Belgique 11.2
Italie Italien 60.8 -- ((pays, langue),
-- ((pays, langue), (pays), ()) France Français 66.1
Suisse Italien 0.5 -- (pays), (langue), ())
ORDER BY langue, pays France 66.1
Belgique Néerlandais 7 ORDER BY pays, langue
LIMIT 12; Italie Italien 60.8
Pays-Bas Néerlandais 16.9 LIMIT 13; Italie 60.8
Allemagne 81.5
Pays-Bas Néerlandais 16.9

21 22

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Numérotation selon un tri


Fonction de fenêtrages window functions
— syntaxe : RANK() OVER (ORDER BY ...)
— accès aux tuples voisins dans SELECT id val rank
un peu comme GROUP BY, mais sans le regroupement SELECT *, 1 10 5

— numérotation selon un tri, une partition, les deux. . . RANK() OVER (ORDER BY val DESC) 2 15 2
FROM Notes 3 17 1
— syntaxe : fonctions et clauses
ORDER BY id;
4 13 4
fonctions d’aggrégation COUNT SUM. . . 5 15 2

fonctions spécifiques RANK LAG. . . SELECT *, id val r1 r2 r3


RANK() OVER (ORDER BY val ASC) AS r1,
clause FILTER(WHERE ...) filtrage 1 10 1 1 5
RANK() OVER (ORDER BY val ASC, id ASC) AS r2,
2 15 3 3 3
clause OVER (...) fenêtre de tuples RANK() OVER (ORDER BY val DESC, id DESC)
3 17 5 5 1
AS r3
nommage clause WINDOW ... AS (...) pour réutilisation FROM Notes 4 13 2 2 4
ORDER BY id; 5 15 3 4 2

23 24
Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Regroupement selon une partition

— syntaxe : AVG(...) OVER (PARTITION BY ...)


Calculer la valeur médiane (le tuple médian)
eleve cours note avc ave
SELECT eleve, cours, note, nom delais
Susie Maths 19 12.25 18.5 avg
-- moyenne par cours
Susie Physics 18 13.25 18.5 calvin 8
AVG(note) OVER (PARTITION BY cours) 24697.600000000000
Hobbes Physics 19 13.25 17.5 mum 13
AS avc, nom delais
-- moyenne par élève Hobbes Maths 16 12.25 17.5 dad 7
suzy 10
AVG(note) OVER (PARTITION BY eleve) Calvin Physics 11 13.25 10.5 suzy 10
AS ave hobbes 123450
Calvin Maths 10 12.25 10.5
FROM Notations
Moe Physics 5 13.25 4.5
ORDER BY ave DESC, note DESC;
Moe Maths 4 12.25 4.5

25 26

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Aggrégations sur groupes ordonnés Générer une somme partielle


— Fonctions mode, percentile cont, percentile disc
id quand montant description sum
WITHIN GROUP (ORDER BY ...)
1 2005-02-01 00:00:00 1000.00 versement initial 1000.00

SELECT * 2 2005-05-09 00:00:00 -100.00 tirage 900.00

FROM AttentePatients 3 2005-05-11 00:00:00 -450.00 sous-tirage 450.00

WHERE delais = ( -- attente médiane 4 2006-01-01 00:00:00 200.00 ouf 650.00

5 2006-12-23 00:00:00 -700.00 joyeux noel -50.00


SELECT percentile disc(0.5) WITHIN GROUP (ORDER BY delais)
6 2007-02-28 00:00:00 123.45 des sous ! 73.45
FROM AttentePatients);

27 28

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Jointure avec opérateur ≤ Avec une fenêtre – window

— associe tous les précédents à chaque opération SELECT id, quand, montant, description,
— complexité en n2 . . . plus ou moins inutilisable SUM(montant) OVER (ORDER BY id ASC)
— préférer une solution applicative ? FROM CompteCheque;
id quand montant description sum
-- jointure speciale...
1 2005-02-01 00:00:00 1000.00 versement initial 1000.00
SELECT cc.id, cc.quand, cc.montant, cc.description,
2 2005-05-09 00:00:00 -100.00 tirage 900.00
SUM(run.montant)
3 2005-05-11 00:00:00 -450.00 sous-tirage 450.00
FROM CompteCheque AS cc
4 2006-01-01 00:00:00 200.00 ouf 650.00
JOIN CompteCheque AS run ON run.id<=cc.id
5 2006-12-23 00:00:00 -700.00 joyeux noel -50.00
GROUP BY cc.id, cc.quand, cc.montant, cc.description
6 2007-02-28 00:00:00 123.45 des sous ! 73.45
ORDER BY cc.id ASC;

29 30

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

Générer une moyenne mobile Requête récursive et fermeture transitive


yr qt ma

1980 3.1 3.6 WITH nommage d’une requête temporaire


1981 4.2 4.2 — sorte de vue locale à une requête, de sous-requête
SELECT *, 1982 5.3 5.2 — calcul indépendant, stockage dans une table temporaire
AVG(qt) OVER (ORDER BY yr
1983 6.2 6.2
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) — utilisation à la suite immédiate
1984 7.1 7.1
AS ma — peut inclure INSERT, UPDATE, DELETE
FROM oilprod 1985 7.9 7.7

ORDER BY yr ASC; 1986 8.1 8.1 — attention, barrière d’optimisation : implémentation matérialisée
1987 8.3 8.1 WITH RECURSIVE calcul itératif. . .
1988 7.8 7.9
— calcul fermeture transitive : arrêt quand ajout vide
1989 7.6 7.7

31 32
Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

WITH : moyenne inférieure à la moyenne


— réutilisation locale d’une sous-requête. . .
WITH RECURSIVE : fermeture transitive
WITH auteur moy(auteur,moy) AS (
SELECT nom, AVG(durée) — table EnfantDe des ascendants directs
FROM Films NATURAL JOIN Personnes — calcul des ascendants et leur degré enfant parent
GROUP BY nom) — stockage table Ascendant Calvin Dad
SELECT auteur, moy CREATE TABLE Ascendant( Calvin Mum
FROM auteur moy enfant TEXT NOT NULL, Granny Great Granny

WHERE moy < (SELECT AVG(moy) FROM auteur moy) parent TEXT NOT NULL, Mum Grand Pa
degre INTEGER NOT NULL,
ORDER BY auteur; Mum Granny
PRIMARY KEY(enfant,parent)
auteur moy );
Allen 01:42:00

Ozu 01:37:00

33 34

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

WITH RECURSIVE : exemple

WITH RECURSIVE Ascend(enfant,parent,degre)


AS (
WITH RECURSIVE : contraintes
enfant parent degre
-- initialisation
— WITH RECURSIVE démarrage
SELECT enfant, parent, 1 Calvin Dad 1
FROM EnfantDe Calvin Mum 1 — SELECT simple, contenu initial
UNION Granny Great Granny 1 — UNION avec l’incrément d’itération
-- itérations
Mum Grand Pa 1 — SELECT sur la table elle-même
SELECT ed.enfant, a.parent, 1+a.degre
Mum Granny 1
FROM Ascend AS a qui ne doit apparaı̂tre qu’une fois !
JOIN EnfantDe AS ed ON (ed.parent=a.enfant) Calvin Grand Pa 2
— calcul fermeture transitive :
) Calvin Granny 2
-- stocke le résultat dans Ascendant Mum Great Granny 2 implémentation par une boucle sur une table temporaire
INSERT INTO Ascendant(enfant, parent, degre)
Calvin Great Granny 3
SELECT enfant, parent, degre
FROM Ascend;

35 36

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

WITH avec INSERT UPDATE DELETE Plus longues séquences de températures croissantes
WITH archived AS (
http://tapoueh.org/blog/2018/02/find-the-number-of-the-longest-continuously-rising-days-for-a-stock/
DELETE FROM Invoice
WHERE paid AND sent < CURRENT DATE - INTERVAL ’1 month’ date temp
RETURNING * 2017-01-01 -0.5
)
2017-01-02 1.3
INSERT INTO Archive — variations journalières
2017-01-03 1.1
SELECT * FROM archived;
LAG(c, 1) OVER (ORDER BY d) 2017-01-04 1.4
— longueur des séquences croissantes 2017-01-05 2.4
WITH changed AS (
UPDATE stuff WITH RECURSIVE 2017-01-06 1.1

SET something = ’changed’ 2017-01-07 -2.2


— sélection des plus longues MAX
WHERE condition 2017-01-08 -1.3
RETURNING *
2017-01-09 -1.5
)
2017-01-10 2.0
SELECT * FROM changed;

37 38

Fabien Coelho SQL bien avancé Fabien Coelho SQL bien avancé

WITH RECURSIVE
Delta AS ( -- day-on-day temperature delta
SELECT LAG(date, 1) OVER (ORDER BY date) AS dstart,
PostgreSQL/SQL Turing complet : oui !
date AS dend,
temp - LAG(temp, 1) OVER (ORDER BY date) AS inc http://blog.coelho.net/tags.html#Turing-ref
FROM Temperature),
RaisingTemp AS ( -- increasing temperature sequences — bande semi infinie de symboles, position, état
SELECT dstart, dend, 1 AS cnt
— transitions : nouvel état, symbole et position
FROM Delta
WHERE inc >= 0
UNION boucle . . .
SELECT p.dstart, d.dend, p.cnt + 1 — WITH RECURSIVE pour itérer
FROM RaisingTemp AS p
JOIN Delta AS d ON (p.dend = d.dstart) — MAX OVER pour retrouver le symbole suivant
WHERE d.inc >= 0) — CROSS JOIN pour agrandir la bande
-- keep longest sequences found
SELECT dstart, MAX(cnt) AS cnt récursion avec une fonction SQL
FROM RaisingTemp
GROUP BY dstart
ORDER BY cnt DESC, dstart ASC LIMIT 5;

39 40
Fabien Coelho SQL bien avancé

List of Slides

2 SQL bien avancé


Synchroniser deux relations 2 SQL Hacks

3 Approche orientée problème


hypothèses tables avec clef primaire et autres attributs
4 Valeurs directes (1)
— localement (même base de données)
— à distance (bases hétérogènes) 5 Valeurs directes (2)

6 Valeurs directes (3)


comparaison trouver et analyser les différences
7 Énumérer une série
— INSERT, UPDATE, DELETE
— moyen : requête locale, sommes de contrôles hiérarchiques 8 Génération d’une relation. . .

9 Tableau constant...

10 Requête à côté LATERAL


11 MERGE/UPSERT : INSERT ou UPDATE

41

12 Exemple UPSERT 25 Regroupement selon une partition

13 Héritage entre relations 26 Calculer la valeur médiane (le tuple médian)

14 Remplissage d’une hiérarchie de tables 27 Aggrégations sur groupes ordonnés

15 Extraction sur une hiérarchie de tables 28 Générer une somme partielle

16 Aggrégations partielles FILTER 29 Jointure avec opérateur ≤

17 GROUPING SETS, CUBE, ROLLUP 30 Avec une fenêtre – window

18 Exemple grouping sets 31 Générer une moyenne mobile

19 Cumuls par pays et par langues 32 Requête récursive et fermeture transitive

20 Équivalent avec UNION 33 WITH : moyenne inférieure à la moyenne


21 Exemple ROLLUP 34 WITH RECURSIVE : fermeture transitive
22 Exemple CUBE 35 WITH RECURSIVE : exemple
23 Fonction de fenêtrages window functions 36 WITH RECURSIVE : contraintes
24 Numérotation selon un tri 37 WITH avec INSERT UPDATE DELETE

38 Plus longues séquences de températures croissantes

40 PostgreSQL/SQL Turing complet : oui !

41 Synchroniser deux relations