Plus loin avec SQL

Module S3

Dalibo SCOP

24.12

18 décembre 2024

Sur ce document

Formation Module S3
Titre Plus loin avec SQL
Révision 24.12
PDF https://dali.bo/s3_pdf
EPUB https://dali.bo/s3_epub
HTML https://dali.bo/s3_html
Slides https://dali.bo/s3_slides
TP https://dali.bo/s3_tp
TP (solutions) https://dali.bo/s3_solutions

Licence Creative Commons CC-BY-NC-SA

Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :

  • Paternité
  • Pas d’utilisation commerciale
  • Partage des conditions initiales à l’identique

Marques déposées

PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.

Versions de PostgreSQL couvertes

Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 13 à 17.

Plus loin avec SQL

Préambule

  • Après la définition des objets, leur lecture et leur écriture
  • Aller plus loin dans l’écriture de requêtes avec :
    • les jointures
    • les sous-requêtes
    • les vues
    • les fonctions
  • Valeur NULL
  • Agrégats, GROUP BY, HAVING
  • Sous-requêtes
  • Jointures
  • Expression conditionnelle CASE
  • Opérateurs ensemblistes : UNION, EXCEPT, INTERSECT
  • Fonctions de base
  • Vues
  • Requêtes préparées

Objectifs

  • Comprendre l’intérêt du NULL
  • Savoir écrire des requêtes complexes

Valeur NULL

  • Comment représenter une valeur que l’on ne connaît pas ?
    • valeur NULL
  • Trois sens possibles pour NULL :
    • valeur inconnue
    • valeur inapplicable
    • absence de valeur
  • Logique 3 états

Avertissement

  • Chris J. Date a écrit :
    • La valeur NULL telle qu’elle est implémentée dans SQL peut poser plus de problèmes qu’elle n’en résout. Son comportement est parfois étrange et est source de nombreuses erreurs et de confusions.
  • Éviter d’utiliser NULL le plus possible
    • utiliser NULL correctement lorsqu’il le faut

Assignation de NULL

  • Assignation de NULL pour INSERT et UPDATE
  • Explicitement :
    • NULL est indiqué explicitement dans les assignations
  • Implicitement :
    • la colonne n’est pas affectée par INSERT
    • et n’a pas de valeur par défaut
  • Empêcher la valeur NULL
    • contrainte NOT NULL

Calculs avec NULL

  • Utilisation dans un calcul
    • propagation de NULL
  • NULL est inapplicable
    • le résultat vaut NULL

NULL et les prédicats

  • Dans un prédicat du WHERE :
    • opérateur IS NULL ou IS NOT NULL
  • AND :
    • vaut false si NULL AND false
    • vaut NULL si NULL AND true ou NULL AND NULL
  • OR :
    • vaut true si NULL OR true
    • vaut NULL si NULL OR false ou NULL OR NULL

NULL et les agrégats

  • Opérateurs d’agrégats
    • ignorent NULL
    • sauf count(*)

COALESCE

  • Remplacer NULL par une autre valeur
    • COALESCE(attribut, ...);

Agrégats

  • Regroupement de données
  • Calculs d’agrégats

Regroupement de données

  • Regroupement de données : sql GROUP BY expression [, ...]
  • Chaque groupe de données est ensuite représenté sur une seule ligne
  • Permet d’appliquer des calculs sur les ensembles regroupés
    • comptage, somme, moyenne, etc.

Calculs d’agrégats

  • Effectuent un calcul sur un ensemble de valeurs
    • somme, moyenne, etc.
  • Retournent NULL si l’ensemble est vide
    • sauf count()

Agrégats simples

  • Comptage : sql count(expression)
  • compte les lignes : count(*)
    • compte les valeurs renseignées : count(colonne)
  • Valeur minimale : sql min(expression)
  • Valeur maximale : sql max(expression)

Calculs d’agrégats

  • Moyenne : sql avg(expression)
  • Somme : sql sum(expression)
  • Écart-type : sql stddev(expression)
  • Variance : sql variance(expression)

Agrégats sur plusieurs colonnes

  • Possible d’avoir plusieurs paramètres sur la même fonction d’agrégat
  • Quelques exemples
    • pente : regr_slope(Y,X)
    • intersection avec l’axe des ordonnées : regr_intercept(Y,X)
    • indice de corrélation : corr (Y,X)

Clause HAVING

  • Filtrer sur des regroupements
    • HAVING
  • WHERE s’applique sur les lignes lues
  • HAVING s’applique sur les lignes groupées

Sous-requêtes

  • Corrélation requête/sous-requête
  • Sous-requêtes retournant une seule ligne
  • Sous-requêtes retournant une liste de valeur
  • Sous-requêtes retournant un ensemble
  • Sous-requêtes retournant un ensemble vide ou non-vide

Corrélation requête/sous-requête

  • Fait référence à la requête principale
  • Peut utiliser une valeur issue de la requête principale

Qu’est-ce qu’une sous-requête ?

  • Une requête imbriquée dans une autre requête
  • Le résultat de la requête principale dépend du résultat de la sous-requête
  • Encadrée par des parenthèses : ( et )

Utiliser une seule ligne

  • La sous-requête ne retourne qu’une seule ligne
    • sinon une erreur est levée
  • Positionnée
    • au niveau de la liste des expressions retournées par SELECT
    • au niveau de la clause WHERE
    • au niveau d’une clause HAVING

Utiliser une liste de valeurs

  • La sous-requête retourne
    • plusieurs lignes
    • sur une seule colonne
  • Positionnée
    • avec une clause IN
    • avec une clause ANY
    • avec une clause ALL

Clause IN

expression IN (sous-requete)
  • L’expression de gauche est évaluée et vérifiée avec la liste de valeurs de droite
  • IN vaut true
    • si l’expression de gauche correspond à un élément de la liste de droite
  • IN vaut false
    • si aucune correspondance n’est trouvée et la liste ne contient pas NULL
  • IN vaut NULL
    • si l’expression de gauche vaut NULL
    • si aucune valeur ne correspond et la liste contient NULL

Clause NOT IN

expression NOT IN (sous-requete)
  • L’expression de droite est évaluée et vérifiée avec la liste de valeurs de gauche
  • NOT IN vaut true
    • si aucune correspondance n’est trouvée et la liste ne contient pas NULL
  • NOT IN vaut false
    • si l’expression de gauche correspond à un élément de la liste de droite
  • NOT IN vaut NULL
    • si l’expression de gauche vaut NULL
    • si aucune valeur ne correspond et la liste contient NULL

Clause ANY

expression operateur ANY (sous-requete)
  • L’expression de gauche est comparée au résultat de la sous-requête avec l’opérateur donné
  • La ligne de gauche est retournée
    • si le résultat d’au moins une comparaison est vraie
  • La ligne de gauche n’est pas retournée
    • si aucun résultat de la comparaison n’est vrai
    • si l’expression de gauche vaut NULL
    • si la sous-requête ramène un ensemble vide

Clause ALL

expression operateur ALL (sous-requete)
  • L’expression de gauche est comparée à tous les résultats de la sous-requête avec l’opérateur donné
  • La ligne de gauche est retournée
    • si tous les résultats des comparaisons sont vrais
    • si la sous-requête retourne un ensemble vide
  • La ligne de gauche n’est pas retournée
    • si au moins une comparaison est fausse
    • si au moins une comparaison est NULL

Utiliser un ensemble

  • La sous-requête retourne
    • plusieurs lignes
    • sur plusieurs colonnes
  • Positionnée au niveau de la clause FROM
  • Nommée avec un alias de table

Clause EXISTS

EXISTS (sous-requete)
  • Intéressant avec une corrélation
  • La clause EXISTS vérifie la présence ou l’absence de résultats
    • vrai si l’ensemble est non vide
    • faux si l’ensemble est vide

Jointures

  • Conditions de jointure dans JOIN ou dans WHERE ?
  • Produit cartésien
  • Jointure interne
  • Jointures externes
  • Jointure ou sous-requête ?

Conditions de jointure dans JOIN ou dans WHERE ?

  • Jointure dans clause JOIN
    • séparation nette jointure et filtrage
    • plus lisible et maintenable
    • jointures externes propres
    • facilite le travail de l’optimiseur
  • Jointure dans clause WHERE
    • uistorique

Produit cartésien

  • Clause CROSS JOIN
  • Réalise toutes les combinaisons entre les lignes d’une table et les lignes d’une autre
  • À éviter dans la mesure du possible
    • peu de cas d’utilisation
    • peu performant

Jointure interne

  • Clause INNER JOIN
    • meilleure lisibilité
    • facilite le travail de l’optimiseur
  • Joint deux tables entre elles
    • Selon une condition de jointure
Schéma de jointure interne

Syntaxe d’une jointure interne

  • Condition de jointure par prédicats : sql table1 [INNER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées : sql table1 [INNER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes de même nom (dangereux) : sql table1 NATURAL [INNER] JOIN table2

Jointure externe

  • Jointure externe à gauche
    • ramène le résultat de la jointure interne
    • ramène l’ensemble de la table de gauche qui ne peut être joint avec la table de droite
    • les attributs de la table de droite sont alors NULL
Schéma de jointure externe gauche

Jointure externe - 2

  • Jointure externe à droite
    • ramène le résultat de la jointure interne
    • ramène l’ensemble de la table de droite qui ne peut être joint avec la table de gauche
    • les attributs de la table de gauche sont alors NULL
Schéma de jointure externe droite

Jointure externe complète

  • Ramène le résultat de la jointure interne
  • Ramène l’ensemble de la table de gauche qui ne peut être joint avec la table de droite
    • les attributs de la table de droite sont alors NULL
  • Ramène l’ensemble de la table de droite qui ne peut être joint avec la table de gauche
    • les attributs de la table de gauche sont alors NULL

Syntaxe d’une jointure externe à gauche

  • Condition de jointure par prédicats : sql table1 LEFT [OUTER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées : sql table1 LEFT [OUTER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites : sql table1 NATURAL LEFT [OUTER] JOIN table2

Syntaxe d’une jointure externe à droite

  • Condition de jointure par prédicats : sql table1 RIGHT [OUTER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées : sql table1 RIGHT [OUTER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites : sql table1 NATURAL RIGHT [OUTER] JOIN table2

Syntaxe d’une jointure externe complète

  • Condition de jointure par prédicats : sql table1 FULL OUTER JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées : sql table1 FULL OUTER JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites : sql table1 NATURAL FULL OUTER JOIN table2

Jointure ou sous-requête ?

  • Jointures
    • algorithmes très efficaces
    • ne gèrent pas tous les cas
  • Sous-requêtes
    • parfois peu performantes
    • répondent à des besoins non couverts par les jointures

Expressions CASE

  • Équivalent à l’instruction switch en C ou Java
  • Emprunté au langage Ada
  • Retourne une valeur en fonction du résultat de tests

CASE simple

CASE expression
     WHEN valeur THEN expression
     WHEN valeur THEN expression
     (...)
     ELSE expression
 END

CASE sur expressions

CASE WHEN expression THEN expression
     WHEN expression THEN expression
     (...)
     ELSE expression
 END

Spécificités de CASE

  • Comportement procédural
    • les expressions sont évaluées dans l’ordre d’apparition
  • Transtypage
    • le type du retour de l’expression dépend du type de rang le plus élevé de toute l’expression
  • Imbrication
    • des expressions CASE à l’intérieur d’autres expressions CASE
  • Clause ELSE
    • recommandé

Opérateurs ensemblistes

  • UNION
  • INTERSECT
  • EXCEPT

Regroupement de deux ensembles

  • Regroupement avec dédoublonnage :
requete_select1 UNION requete_select2
  • Regroupement sans dédoublonnage :
requete_select1 UNION ALL requete_select2

Intersection de deux ensembles

  • Intersection de deux ensembles avec dédoublonnage :
requete_select1 INTERSECT requete_select2
  • Intersection de deux ensembles sans dédoublonnage :
requete_select1 INTERSECT ALL requete_select2

Différence entre deux ensembles

  • Différence entre deux ensembles avec dédoublonnage :
requete_select1 EXCEPT requete_select2
  • Différence entre deux ensembles sans dédoublonnage :
requete_select1 EXCEPT ALL requete_select2

Fonctions de base

  • Transtypage
  • Manipulation de chaines
  • Manipulation de types numériques
  • Manipulation de dates
  • Génération de jeu de données

Transtypage

  • Conversion d’un type de données vers un autre type de données
  • CAST (expression AS type)
  • expression::type

Opérations simples sur les chaînes

  • Concaténation : chaîne1 || chaîne2
  • Longueur de la chaîne : char_length(chaîne)
  • Conversion en minuscules : lower(chaîne)
  • Conversion en majuscules : upper(chaîne)

Manipulations de chaînes

  • Extrait une chaîne à partir d’une autre : substring(chaîne [from int] [for int])
  • Emplacement d’une sous-chaîne : position(sous-chaîne in chaîne)

Manipulation de types numériques

  • Opérations arithmétiques
  • Manipulation de types numériques
  • Génération de données

Opérations arithmétiques

  • Addition : +
  • Soustraction : -
  • Multiplication : *
  • Division : /
    • entière si implique des entiers !
  • Reste (modulo) : %

Fonctions numériques courantes

  • Arrondi : round(numeric)

  • Troncature : trunc(numeric [, precision])

  • Entier le plus petit : floor(numeric)

  • Entier le plus grand : ceil(numeric)

Génération de données

-- Suite d'entiers, 1 par ligne
SELECT i   FROM   generate_series(0, 100, 1) i ;

-- Nombre entre 0 et 1.0
SELECT random() ;
-- Entre 1 et 100
SELECT (random()*100)::int ;

-- Entre 2 bornes (v17+)
SELECT random (20.0, 50.0) ;  -- numeric
SELECT random (-100, 100) ;   -- entier

Manipulation de dates

  • Obtenir la date et l’heure courante
  • Manipuler des dates
  • Opérations arithmétiques
  • Formatage de données

Date et heure courante

  • Retourne la date courante : current_date
  • Retourne l’heure courante : current_time
  • Retourne la date et l’heure courante : current_timestamp / now()

Manipulation des données

  • Âge
    • Par rapport à la date courante : age(timestamp)
    • Par rapport à une date de référence : age(timestamp, timestamp)

Tronquer et extraire

  • Troncature d’une date : date_trunc(text, timestamp)
  • Exemple : date_trunc('month' from date_naissance)
  • Extrait une composante de la date : extract(text, timestamp)
  • Exemple : extract('year' from date_naissance)

Arithmétique sur les dates

  • Opérations arithmétiques sur timestamp, time ou date
    • date/time - date/time = interval
    • date/time + time = date/time
    • date/time + interval = date/time
  • Opérations arithmétiques sur interval
    • interval * numeric = interval
    • interval / numeric = interval
    • interval + interval = interval

Date vers chaîne

  • Conversion d’une date en chaîne de caractères : to_char(timestamp, text)
  • Exemple : to_char(current_timestamp, 'DD/MM/YYYY HH24:MI:SS')

Chaîne vers date

  • Conversion d’une chaîne de caractères en date : to_date(text, text) to_date('05/12/2000', 'DD/MM/YYYY')
  • Conversion d’une chaîne de caractères en timestamp : to_timestamp(text, text) to_timestamp('05/12/2000 12:00:00', 'DD/MM/YYYY HH24:MI:SS')
  • Paramètre datestyle

Génération de données

  • Générer une suite de timestamp :
    • generate_series (timestamp_debut, timestamp_fin, intervalle)

Vues

  • Tables virtuelles
    • définies par une requête SELECT
    • définition stockée dans le catalogue de la base de données
  • Objectifs
    • masquer la complexité d’une requête
    • masquer certaines données à l’utilisateur
  • Vues ≠ vues matérialisées

Création d’une vue

  • Une vue porte un nom au même titre qu’une table
    • elle sera nommée avec les mêmes règles
  • Ordre de création d’une vue : CREATE VIEW vue (colonne ...) AS SELECT ...

Lecture d’une vue

  • Une vue est lue comme une table
    • SELECT * FROM vue;

Sécurisation d’une vue

  • Sécuriser une vue
    • droits avec GRANT et REVOKE
  • Utiliser les vues comme moyen de filtrer les lignes est dangereux
    • option security_barrier

Mise à jour des vues

  • Updatable view
  • WITH CHECK OPTION
  • Mises à jour non triviales : trigger INSTEAD OF

Mauvaises utilisations des vues

  • Prolifération des vues
    • créer une vue doit se justifier
    • ne pas créer une vue par table
  • Vues trop complexes utilisées comme interface
  • Vues empilées

Requêtes préparées

  • Exécution en deux temps
    • préparation du plan d’exécution de la requête
    • exécution de la requête en utilisant le plan préparé
  • Objectif :
    • éviter simplement les injections SQL
    • améliorer les performances

Utilisation

  • PREPARE, préparation du plan d’exécution d’une requête
  • EXECUTE, passage des paramètres de la requête et exécution réelle
  • L’implémentation dépend beaucoup du langage de programmation utilisé
    • le connecteur JDBC supporte les requêtes préparées
    • le connecteur PHP/PDO également

Conclusion

  • Possibilité d’écrire des requêtes complexes
  • C’est là où PostgreSQL est le plus performant

Questions

N’hésitez pas, c’est le moment !

Travaux pratiques

TP 1

TP 2

Travaux pratiques (solutions)

TP 1

TP 2