SQL conception et mise en œuvre

Formation SQL1

Dalibo SCOP

8 janvier 2018

Licence Creative Commons CC-BY-NC-SA

Vous êtes libres de redistribuer et/ou modifier cette création selon les conditions suivantes :

Introduction et premiers SELECT

Préambule

  • Qu'est-ce que le standard SQL ?
  • Comment lire des données
  • Quel type de données est disponible ?
  • Principes d'une base de données
  • Premières requêtes
  • Connaître les types de données

Objectifs

  • Comprendre les principes
  • Écrire quelques requêtes en lecture
  • Connaître les différents types de données
    • et quelques fonctions très utiles

Principes d'une base de données

  • Base de données
    • ensemble organisé d'informations
  • Système de Gestion de Bases de Données
    • acronyme SGBD (DBMS en anglais)
    • programme assurant la gestion et l'accès à une base de données
    • assure la cohérence des données

Type de bases de données

  • Modèle hiérarchique
  • Modèle réseau
  • Modèle relationnel
  • Modèle objet
  • Modèle relationnel-objet
  • NoSQL

Type de bases de données (1)

  • Modèle hiérarchique
    • structure arborescente
    • redondance des données
  • Modèle réseau
    • structure arborescente, mais permettant des associations
    • ex : Bull IDS2 sur GCOS

Type de bases de données (2)

  • Modèle relationnel
    • basé sur la théorie des ensembles et la logique des prédicats
    • standardisé par la norme SQL
  • Modèle objet
    • structure objet
    • pas de standard
  • Modèle relationnel-objet
    • le standard SQL ajoute des concepts objets

Type de bases de données (3)

  • NoSQL : « Not only SQL »
    • pas de norme de langage de requête
    • clé-valeur (Redis, Riak)
    • graphe (Neo4J)
    • document (MongoDB, CouchDB)
    • orienté colonne (HBase)
  • Rapprochement relationnel/NoSQL
    • PostgreSQL permet de stocker des documents (JSON, XML)

Modèle relationnel

  • Indépendance entre la vue logique et la vue physique
    • le SGBD gère lui-même le stockage physique
  • Table ou relation
  • Un ensemble de tables représente la vue logique

Caractéristiques du modèle relationnel

  • Théorie des ensembles
  • Logique des prédicats
  • Logique 3 états

ACID

  • Atomicité (Atomic)
  • Cohérence (Consistent)
  • Isolation (Isolated)
  • Durabilité (Durable)

Langage SQL

  • Norme ISO 9075
    • dernière version stable : 2016
  • Langage déclaratif
    • on décrit le résultat et pas la façon de l'obtenir
    • comme Prolog
  • Traitement ensembliste
    • par opposition au traitement procédural
    • « on effectue des opérations sur des relations pour obtenir des relations »

SQL est un langage

  • Langage
    • règles d'écriture
    • règles de formatage
    • commentaires
  • Améliore la lisibilité d'une requête

Recommandations d'écriture et de formatage

  • Écriture
    • mots clés SQL en MAJUSCULES
    • identifiants de colonnes/tables en minuscule
  • Formatage
    • dissocier les éléments d'une requête
    • un prédicat par ligne
    • indentation

Commentaires

  • Commentaire sur le reste de la ligne

    -- commentaire
  • Commentaire dans un bloc

    /* bloc
     */

Les 4 types d'ordres SQL

  • DDL
    • Data Definition Language
    • définit les structures de données
  • DML
    • Data Manipulation Language
    • manipule les données
  • DCL
    • Data Control Language
    • contrôle l'accès aux données
  • TCL
    • Transaction Control Language
    • contrôle les transactions

Lecture de données

  • Ordre SELECT
    • lecture d'une ou plusieurs tables
    • ou appel de fonctions

Syntaxe de SELECT

SELECT expressions_colonnes
 [ FROM elements_from ]
 [ WHERE predicats ]
 [ ORDER BY expressions_orderby ]
 [ LIMIT limite ]
 [ OFFSET offset ];

Liste de sélection

  • Description du résultat de la requête
    • colonnes retournées
    • renommage
    • dédoublonnage

Colonnes retournées

  • Liste des colonnes retournées
    • expression
    • séparées par une virgule
  • Expression
    • constante
    • référence de colonne :

      table.colonne
  • opération sur des colonnes et/ou des constantes

Alias de colonne

  • Renommage
    • ou alias
    • AS :

      expression AS alias
  • le résultat portera le nom de l'alias

Dédoublonnage des résultats

  • Dédoublonnage des résultats avant de les retourner
    • DISTINCT
    • à ne pas utiliser systématiquement
  • SELECT DISTINCT expressions_colonnes...

Dérivation

  • SQL permet de dériver les valeurs des colonnes
    • opérations arithmétiques : +, -, /, *
    • concaténation de chaînes : ||
    • appel de fonction

Fonctions utiles

  • Fonctions sur données temporelles :
    • date et heure courante : now()
    • âge : age(timestamp)
    • extraire une partie d'une date : extract( 'year' FROM timestamp)
    • ou date_part('Y',timestamp)
  • Fonctions sur données caractères :
    • longueur d'une chaîne de caractère : char_length(chaine)
  • Compter les lignes : count(*)

Clause FROM

FROM expression_table [, expression_table ...]
  • Description des tables mises en œuvre dans la requête
    • une seule table
    • plusieurs tables jointes
    • sous-requête

Alias de table

  • mot-clé AS
    • optionnel :

      reference_table alias
  • la table sera ensuite référencée par l'alias

    reference_table [AS] alias
    reference_table AS alias (alias_colonne1, ...)

Nommage des objets

  • Noms d'objets convertis en minuscules
    • Nom_Objet devient nom_objet
    • certains nécessitent l'emploi de majuscules
  • Le guillemet double " conserve la casse
    • "Nom_Objet"

Clause WHERE

  • Permet d'exprimer des conditions de filtrage
    • prédicats
  • Un prédicat est une opération logique
    • renvoie vrai ou faux
  • La ligne est présente dans le résultat
    • si l'expression logique des prédicats est vraie

Expression et opérateurs de prédicats

  • Comparaison
    • =, <, >, =<, >=, <>
  • Négation
    • NOT
expression operateur_comparaison expression

Combiner des prédicats

  • OU logique
    • predicat OR predicat
  • ET logique
    • predicat AND predicat

Correspondance de motif

  • Comparaison de motif

    chaine LIKE motif ESCAPE 'c'
  • % : toute chaine de 0 à plusieurs caractères
    • _ : un seul caractère
  • Expression régulière POSIX

    chaine ~ motif

Listes et intervalles

  • Liste de valeurs

    expression IN (valeur1 [, ...])
  • Chevauchement d'intervalle de valeurs

    expression BETWEEN expression AND expression
  • Chevauchement d'intervalle de dates

    (date1, date2) OVERLAPS (date3, date4)

Tris

  • SQL ne garantit pas l'ordre des résultats
    • tri explicite requis
  • Tris des lignes selon des expressions

    ORDER BY expression  [ ASC | DESC | USING opérateur ]
                         [ NULLS { FIRST | LAST } ] [, ...]
  • ordre du tri : ASC ou DESC
    • placement des valeurs NULL : NULLS FIRST ou NULLS LAST
    • ordre de tri des caractères : COLLATE collation

Limiter le résultat

  • Obtenir des résultats à partir de la ligne n
    • OFFSET n
  • Limiter le nombre de lignes à n lignes
    • FETCH {FIRST | NEXT} n ROWS ONLY
    • LIMIT n
  • Opérations combinables
    • OFFSET doit apparaitre avant FETCH
  • Peu d'intérêt sur des résultats non triés

Utiliser plusieurs tables

  • Clause FROM
    • liste de tables séparées par ,
  • Une table est combinée avec une autre
    • jointure
    • produit cartésien

Types de données

  • Type de données
    • du standard SQL
    • certains spécifiques PostgreSQL

Qu'est-ce qu'un type de données ?

  • Le système de typage valide les données
  • Un type détermine
    • les valeurs possibles
    • comment les données sont stockées
    • les opérations que l'on peut appliquer

Types de données

  • Types standards SQL
  • Types dérivés
  • Types spécifiques à PostgreSQL
  • Types utilisateurs

Types standards (1)

  • Caractère
    • char, varchar
  • Numérique
    • integer, smallint, bigint
    • real, double precision
    • numeric, decimal
  • Booléen
    • boolean

Types standards (2)

  • Temporel
    • date, time
    • timestamp
    • interval
  • Chaînes de bit
    • bit, bit varying
  • Formats validés
    • XML

Caractères

  • char(n)
    • longueur fixe
    • de n caractères
    • complété à droite par des espaces si nécessaire
  • varchar(n)
    • longueur variable
    • maximum n caractères
    • n optionnel

Représentation données caractères

  • Norme SQL
    • chaîne encadrée par '
    • 'chaîne de caractères'
  • Chaînes avec échappement du style C
    • chaîne précédée par E ou e
    • E'chaîne de caractères'
  • Chaînes avec échappement Unicode
    • chaîne précédée par U&
    • U&'chaîne de caractères'

Numériques

  • Entier
    • smallint, integer, bigint
    • signés
  • Virgule flottante
    • real, double precision
    • valeurs inexactes
  • Précision arbitraire
    • numeric(precision, echelle), decimal(precision, echelle)
    • valeurs exactes

Représentation de données numériques

  • Chiffres décimaux : 0 à 9
  • Séparateur décimal : .
  • chiffres
  • chiffres.[chiffres][e[+-]chiffres]
  • [chiffres].chiffres[e[+-]chiffres]
  • chiffrese[+-]chiffres
  • Conversion
    • TYPE 'chaine'

Booléens

  • boolean
  • 3 valeurs possibles
    • TRUE
    • FALSE
    • NULL (ie valeur absente)

Temporel

  • Date
    • date
  • Heure
    • time
    • avec ou sans fuseau horaire
  • Date et heure
    • timestamp
    • avec ou sans fuseau horaire
  • Intervalle de temps
    • interval

Représentation des données temporelles

  • Conversion explicite
    • TYPE 'chaine'
  • Format d'un timestamp
    • 'YYYY-MM-DD HH24:MI:SS.ssssss'
    • 'YYYY-MM-DD HH24:MI:SS.ssssss+fuseau'
    • 'YYYY-MM-DD HH24:MI:SS.ssssss' AT TIME ZONE 'fuseau'
  • Format d'un intervalle
    • INTERVAL 'durée interval'

Gestion des fuseaux horaires

  • Paramètre timezone
  • Session : SET TIME ZONE
  • Expression d'un fuseau horaire
    • nom complet : 'Europe/Paris'
    • nom abbrégé : 'CEST'
    • décalage : '+02'

Chaînes de bits

  • Chaînes de bits
    • bit(n), bit varying(n)

Représentation des chaînes de bits

  • Représentation binaire
    • Chaîne de caractères précédée de la lettre B
    • B'01010101'
  • Représentation hexadécimale
    • Chaîne de caractères précédée de la lettre X
    • X'55'

XML

  • Type validé
    • xml
  • Chaîne de caractères
    • validation du document XML

Types dérivés

  • Types spécifiques à PostgreSQL
  • Sériés
    • principe de l'« autoincrement »
    • serial
    • smallserial
    • bigserial
    • équivalent à un type entier associé à une séquence et avec une valeur par défaut
    • (v 10+) préférer un type entier + la propriété IDENTITY
  • Caractères
    • text

Types additionnels non SQL

  • bytea
  • array
  • enum
  • cidr, inet, macaddr
  • uuid
  • json
  • jsonb
  • hstore
  • range

Types utilisateurs

  • Types utilisateurs
    • composites
    • énumérés (enum)
    • intervalles (range)
    • scalaires
    • tableau

      CREATE TYPE

Conclusion

  • SQL : traitement d'ensembles d'enregistrements
  • Pour les lectures : SELECT
  • Nom des objets en minuscules
  • Des types de données simples et d'autres plus complexes

Bibliographie

  • Bases de données - de la modélisation au SQL (Laurent Audibert)
  • SQL avancé : programmation et techniques avancées (Joe Celko)
  • SQL : Au coeur des performances (Markus Winand)
  • The Manga Guide to Databases (Takahashi, Mana, Azuma, Shoko)
  • The Art of SQL (Stéphane Faroult)

Questions

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

Travaux Pratiques

Création d'objet et mises à jour

Introduction

  • DDL, gérer les objets
  • DML, écrire des données
  • Gérer les transactions
  • DDL (Data Definition Language)
  • DML (Data Manipulation Language)
  • TCL (Transaction Control Language)

Objectifs

  • Savoir créer, modifier et supprimer des objets
  • Savoir utiliser les contraintes d'intégrité
  • Savoir mettre à jour les données
  • Savoir utiliser les transactions

DDL

  • DDL
    • Data Definition Language
    • langage de définition de données
  • Permet de créer des objets dans la base de données

Objets d'une base de données

  • Objets définis par la norme SQL :
    • schémas
    • séquences
    • tables
    • contraintes
    • domaines
    • vues
    • fonctions
    • triggers

Créer des objets

  • Ordre CREATE
  • Syntaxe spécifique au type d'objet
  • Exemple :
CREATE SCHEMA s1;

Modifier des objets

  • Ordre ALTER
  • Syntaxe spécifique pour modifier la définition d'un objet, exemple:
  • renommage

    ALTER type_objet ancien_nom RENAME TO nouveau_nom ;
  • changement de propriétaire

    ALTER type_objet nom_objet OWNER TO proprietaire ;
  • changement de schéma

    ALTER type_objet nom_objet SET SCHEMA nom_schema ;

Supprimer des objets

  • Ordre DROP
  • Exemples :
    • supprimer un objet :

      DROP type_objet nom_objet ;
  • supprimer un objet et ses dépendances :

    DROP type_objet nom_objet CASCADE ;

Schéma

  • Identique à un espace de nommage
  • Permet d'organiser les tables de façon logique
  • Possibilité d'avoir des objets de même nom dans des schémas différents
  • Pas d'imbrication (contrairement à des répertoires par exemple)
  • Schéma public
    • créé par défaut dans une base de données PostgreSQL

Gestion d'un schéma

  • CREATE SCHEMA nom_schéma
  • ALTER SCHEMA nom_schéma
    • renommage
    • changement de propriétaire
  • DROP SCHEMA [ IF EXISTS ] nom_schéma [ CASCADE ]

Accès aux objets

  • Nommage explicite
    • nom_schema.nom_objet
  • Chemin de recherche de schéma
    • paramètre search_path
    • SET search_path = schema1,schema2,public;
    • par défaut : $user, public

Séquences

  • Séquence
    • génère une séquence de nombres
  • Paramètres
    • valeur minimale MINVALUE
    • valeur maximale MAXVALUE
    • valeur de départ START
    • incrément INCREMENT
    • cache CACHE
    • cycle autorisé CYCLE

Création d'une séquence

CREATE SEQUENCE nom [ INCREMENT incrément ]
    [ MINVALUE valeurmin | NO MINVALUE ]
    [ MAXVALUE valeurmax | NO MAXVALUE ]
    [ START [ WITH ] début ]
    [ CACHE cache ]
    [ [ NO ] CYCLE ]
    [ OWNED BY { nom_table.nom_colonne | NONE } ]

Modification d'une séquence

ALTER SEQUENCE nom [ INCREMENT increment ]
    [ MINVALUE valeurmin | NO MINVALUE ]
    [ MAXVALUE valeurmax | NO MAXVALUE ]
    [ START [ WITH ] début ]
    [ RESTART [ [ WITH ] nouveau_début ] ]
    [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { nom_table.nom_colonne | NONE } ]
  • Il est aussi possible de modifier
    • le propriétaire
    • le schéma

Suppression d'une séquence

DROP SEQUENCE nom [, ...]

Séquences, utilisation

  • Obtenir la valeur suivante
    • nextval('nom_sequence')
  • Obtenir la valeur courante
    • currval('nom_sequence')
    • mais nextval() doit être appelé avant dans la même session

Type SERIAL

  • Type serial/bigserial/smallserial
    • séquence générée automatiquement
    • valeur par défaut nextval(...)
  • (v 10+) Préférer un entier avec IDENTITY

Domaines

  • Permet d'associer
    • un type standard
    • et une contrainte (optionnelle)
Un domaine est utilisé pour définir un type utilisateur qui est en fait un

Tables

  • Équivalent ensembliste d'une relation
  • Composé principalement de
    • colonnes ordonnées
    • contraintes

Création d'une table

  • Définition de son nom
  • Définition de ses colonnes
    • nom, type, contraintes éventuelles
  • Clauses de stockage
  • CREATE TABLE

CREATE TABLE

CREATE TABLE nom_table (
  definition_colonnes
  definition_contraintes
) clause_stockage;

Définition des colonnes

nom_colonne type [ COLLATE collation ] [ contrainte ]
[, ...]

Valeur par défaut

  • DEFAULT
    • affectation implicite
  • Utiliser directement par les types sériés

Copie de la définition d'une table

  • Création d'une table à partir d'une autre table
    • CREATE TABLE ... (LIKE table clause_inclusion)
  • Avec les valeurs par défaut des colonnes :
    • INCLUDING DEFAULTS
  • Avec ses autres contraintes :
    • INCLUDING CONSTRAINTS
  • Avec ses index :
    • INCLUDING INDEXES

Modification d'une table

  • ALTER TABLE
  • Définition de la table
    • renommage de la table
    • ajout/modification/suppression d'une colonne
    • déplacement dans un schéma différent
    • changement du propriétaire
  • Définition des colonnes
    • renommage d'une colonne
    • changement de type d'une colonne
  • Définition des contraintes
    • ajout/suppression d'une contrainte

Suppression d'une table

  • Supprimer une table :

    DROP TABLE nom_table;
  • Supprimer une table et tous les objets dépendants :

    DROP TABLE nom_table CASCADE;

Contraintes d'intégrité

  • ACID
    • Cohérence
    • une transaction amène la base d'un état stable à un autre
  • Assurent la cohérence des données
    • unicité des enregistrements
    • intégrité référentielle
    • vérification des valeurs
    • identité des enregistrements
    • règles sémantiques

Clés primaires

  • Identifie une ligne de manière unique
  • Une seule clé primaire par table
  • Une ou plusieurs colonnes
  • À choisir parmi les clés candidates
    • parfois, utiliser une clé artificielle

Déclaration d'une clé primaire

Construction :

[CONSTRAINT nom_contrainte]
PRIMARY KEY ( nom_colonne [, ... ] )

Contrainte d'unicité

  • Garantie l'unicité des valeurs d'une ou plusieurs colonnes
  • Permet les valeurs NULL
  • Clause UNIQUE
  • Contrainte UNIQUE != index UNIQUE

Déclaration d'une contrainte d'unicité

Construction :

[ CONSTRAINT nom_contrainte]
{ UNIQUE ( nom_colonne [, ... ] )

Intégrité référentielle

  • Contrainte d'intégrité référentielle
    • ou Clé étrangère
  • Référence une clé primaire ou un groupe de colonnes UNIQUE et NOT NULL
  • Garantie l'intégrité des données
  • FOREIGN KEY

Exemple

Schéma base cave

Déclaration d'une clé étrangère

[ CONSTRAINT nom_contrainte ] FOREIGN KEY ( nom_colonne [, ...] )
    REFERENCES table_reference [ (colonne_reference [, ... ] ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
    [ ON DELETE action ] [ ON UPDATE action ] }

Vérification simple ou complète

  • Vérification complète ou partielle d'une clé étrangère
  • MATCH
    • MATCH FULL (complète)
    • MATCH SIMPLE (partielle)

Colonnes d'identité

  • Identité d'un enregistrement
  • GENERATED ... AS IDENTITY
    • ALWAYS
    • BY DEFAULT
  • Préférer à serial
  • Unicité non garantie sans contrainte explicite !

Mise à jour de la clé primaire

  • Que faire en cas de mise à jour d'une clé primaire ?
    • les clés étrangères seront fausses
  • ON UPDATE
  • ON DELETE
  • Définition d'une action au niveau de la clé étrangère
    • interdiction
    • propagation de la mise à jour
    • NULL
    • valeur par défaut

Vérifications

  • Présence d'une valeur
    • NOT NULL
  • Vérification de la valeur d'une colonne
    • CHECK

Vérifications différés

  • Vérifications après chaque ordre SQL
    • problèmes de cohérence
  • Différer les vérifications de contraintes
    • clause DEFERRABLE, NOT DEFERRABLE
    • INITIALLY DEFERED, INITIALLY IMMEDIATE

Vérifications plus complexes

  • Un trigger
    • si une contrainte porte sur plusieurs tables
    • si sa vérification nécessite une sous-requête
  • Préférer les contraintes déclaratives

DML : mise à jour des données

  • SELECT peut lire les données d'une table ou plusieurs tables
    • mais ne peut pas les mettre à jour
  • Ajout de données dans une table
    • INSERT
  • Modification des données d'une table
    • UPDATE
  • Suppression des données d'une table
    • DELETE

Ajout de données : INSERT

  • Ajoute des lignes à partir des données de la requête
  • Ajoute des lignes à partir d'une requête SELECT
  • Syntaxe :
INSERT INTO nom_table [ ( nom_colonne [, ...] ) ]
    { liste_valeurs | requete }

INSERT avec liste d'expressions

INSERT INTO nom_table [ ( nom_colonne [, ...] ) ]
    VALUES ( { expression | DEFAULT } [, ...] ) [, ...]

INSERT à partir d'un SELECT

INSERT INTO nom_table [ ( nom_colonne [, ...] ) ]
    requête

INSERT et colonnes implicites

  • L'ordre physique peut changer dans le temps
    • résultats incohérents
    • requêtes en erreurs

Mise à jour de données : UPDATE

  • Ordre UPDATE
  • Met à jour une ou plusieurs colonnes d'une même ligne
    • à partir des valeurs de la requête
    • à partir des anciennes valeurs
    • à partir d'une requête SELECT
    • à partir de valeurs d'une autre table

Construction d'UPDATE

UPDATE nom_table
    SET
    {
     nom_colonne = { expression | DEFAULT }
    |
     ( nom_colonne [, ...] ) = ( { expression | DEFAULT } [, ...] )
    } [, ...]
    [ FROM liste_from ]
    [ WHERE condition | WHERE CURRENT OF nom_curseur ]

Suppression de données : DELETE

  • Supprime les lignes répondant au prédicat
  • Syntaxe :
DELETE FROM nom_table [ [ AS ] alias ]
    [ WHERE condition

Clause RETURNING

  • Spécifique à PostgreSQL
  • Permet de retourner les lignes complètes ou partielles résultants de INSERT, UPDATE ou DELETE
  • Syntaxe :
requete_sql RETURNING ( * | expression )

Transactions

  • ACID
    • atomicité
    • un traitement se fait en entier ou pas du tout
  • TCL pour Transaction Control Language
    • valide une transaction
    • annule une transaction
    • points de sauvegarde

Auto-commit et transactions

  • Par défaut, PostgreSQL fonctionne en auto-commit
    • à moins d'ouvrir explicitement une transaction
  • Ouvrir une transaction
    • BEGIN TRANSACTION

Validation ou annulation d'une transaction

  • Valider une transaction
    • COMMIT
  • Annuler une transaction
    • ROLLBACK
  • Sans validation, une transaction est forcément annulée

Programmation

  • Certains langages implémentent des méthodes de gestion des transactions
    • PHP, Java, etc.
  • Utiliser ces méthodes prioritairement

Points de sauvegarde

  • Certains traitements dans une transaction peuvent être annulés
    • mais la transaction est atomique
  • Définir un point de sauvegarde
    • SAVEPOINT nom_savepoint
  • Valider le traitement depuis le dernier point de sauvegarde
    • RELEASE SAVEPOINT nom_savepoint
  • Annuler le traitement depuis le dernier point de sauvegarde
    • ROLLBACK TO SAVEPOINT nom_savepoint

Conclusion

  • SQL : toujours un traitement d'ensembles d'enregistrements
    • c'est le côté relationnel
  • Pour les définitions d'objets
    • CREATE, ALTER, DROP
  • Pour les données
    • INSERT, UPDATE, DELETE

Questions

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

Travaux Pratiques

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
    • avec les requêtes intégrées
  • Valeur NULL
  • Agrégats, GROUP BY, HAVING
  • Sous-requêtes
  • Jointures
  • Expression conditionnelle CASE
  • Opérateurs ensemblistes : UNION, EXCEPT, INTERSECT

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 connait 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 :

    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

  • Effectue un calcul sur un ensemble de valeurs
    • somme, moyenne, etc.
  • Retourne NULL si l'ensemble est vide
    • sauf count()

Agrégats simples

  • Comptage :

    count(expression)
  • compte les lignes : count(*)
    • compte les valeurs renseignées : count(colonne)
  • Valeur minimale :

    min(expression)
  • Valeur maximale :

    max(expression)

Calculs d'agrégats

  • Moyenne :

    avg(expression)
  • Somme :

    sum(expression)
  • Écart-type :

    stddev(expression)
  • Variance :

    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

  • Produit cartésien
  • Jointure interne
  • Jointures externes
  • Jointure ou sous-requête ?

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

Schéma de jointure interne

Syntaxe d'une jointure interne

  • Condition de jointure par prédicats :

    table1 [INNER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées :

    table1 [INNER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites :

    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

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

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 :

    table1 LEFT [OUTER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées :

    table1 LEFT [OUTER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites :

    table1 NATURAL LEFT [OUTER] JOIN table2

Syntaxe d'une jointure externe à droite

  • Condition de jointure par prédicats :

    table1 RIGHT [OUTER] JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées :

    table1 RIGHT [OUTER] JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites :

    table1 NATURAL RIGHT [OUTER] JOIN table2

Syntaxe d'une jointure externe complète

  • Condition de jointure par prédicats :

    table1 FULL OUTER JOIN table2 ON prédicat [...]
  • Condition de jointure implicite par liste des colonnes impliquées :

    table1 FULL OUTER JOIN table2 USING (colonne [, ...])
  • Liste des colonnes implicites :

    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

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

Approfondissement SQL

Préambule

  • Aller encore plus loin
    • tout en évitant le pire
  • Appréhender de nouveaux objets
    • fonctions utiles
    • vues
  • Utiliser des requêtes préparées
  • Fonctions de base
  • Vues
  • Requêtes préparées
  • Index
  • Ce qu'il ne faut pas faire

Objectifs

  • Utiliser des objets avancées
  • Gagner en performance
  • Éviter les pièges les plus fréquents

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 : /
  • 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

  • Générer une suite d'entiers : generate_series(borne_debut, borne_fin, intervalle)

  • Générer un nombre aléatoire : random()

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

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

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

  • Trigger INSTEAD OF
  • Updatable view (PostgreSQL 9.3)

Mauvaises utilisations

  • Prolifération des vues
    • créer une vue doit se justifier
    • ne pas créer une vue par table

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

Indexation

  • Comment gagner en performance
  • Index
    • représentation valeur / pointeur
    • arbre de valeurs

Créer un index

CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ name ]
  ON table_name
  ( { column_name | ( expression ) }
    [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
  [ TABLESPACE tablespace_name ]
  [ WHERE predicate ]

Lecture avec index

  • L'index permet une recherche plus rapide
  • Il n'a besoin de lire que quelques blocs
    • et non pas la table entière

Insertion avec index

  • Accélère la recherche
    • mais ralentit les écritures
  • Une écriture dans la table doit mettre à jour l'index
  • Faire attention à ne pas créer des index inutiles

Tri avec un index

  • Un index ne sert pas qu'à la recherche
  • Les données étant triées, il sert aussi à accélérer les tris

Index partiel

  • N'indexer qu'une partie des données
  • Requête typique : SELECT * FROM taches WHERE fait IS false ORDER BY date_rendu;
  • Index typique : CREATE INDEX ON taches(date_rendu) WHERE fait IS false;

Index fonctionnel

  • Indexer le résultat d'une expression
  • Fonctions immuables seulement
  • Requête typique : SELECT * FROM employes WHERE upper(nom)='DUPONT';
  • Index typique : CREATE INDEX ON employes(upper(nom));

Ce qu'il ne faut pas faire

  • Modélisation
  • Écriture de requêtes
  • Conception de l'application

Absence de contraintes

  • Concerne surtout les clés étrangères
  • Parfois (souvent?) ignorées pour diverses raisons :
    • performances
    • flexibilité du modèle de données
    • compatibilité avec d'autres SGBD
    • héritage de MySQL/MyISAM
    • commodité de développement
  • Conséquences
    • problèmes d'intégrité des données
    • procédures de vérification de cohérence des données

Stockage EAV

  • Entité-Attribut-Valeur
  • Pourquoi
    • flexibilité du modèle de données
    • adapter sans délai ni surcoût le modèle de données
  • Conséquences
    • création d'une table : identifiant, nom_attribut, valeur
    • requêtes abominables et coûteuses
  • Solutions
    • revenir sur la conception du modèle de données
    • utiliser un type de données plus adapté (hstore, jsonb)

Attributs multi-colonnes

  • Pourquoi
    • stocker plusieurs attributs pour une même ligne
    • exemple : les différents numéros de téléphone d'une personne
  • Pratique courante
    • ajouter plusieurs colonnes portant le même nom
  • Conséquences
    • et s'il faut rajouter encore une colonne ?
    • maîtrise de l'unicité des valeurs
    • requêtes complexes à maintenir
  • Solutions
    • créer une table de dépendance
    • utiliser un type tableau

Choix des types de données

  • Objectif
    • représenter des valeurs décimales
  • Pratique courante
    • utiliser le type float ou double
  • Problèmes :
    • les types flottants ne stockent qu'une approximation de la valeur représentée
    • les erreurs d'arrondis se multiplient
    • les requêtes produisent des résultats faux
  • Solutions
    • privilégier le type numeric(x, y) pour les calculs précis (financiers notamment)

Problèmes courants d'écriture de requêtes

  • Utilisation de NULL
  • Ordre implicite des colonnes
  • Requêtes spaghetti
  • Moteur de recherche avec LIKE

Ordre implicite des colonnes

  • Objectif
    • s'économiser d'écrire la liste des colonnes dans une requête
  • Problèmes
    • si l'ordre des colonnes change, les résultats changent
    • résultats faux
    • données corrompues
  • Solutions
    • nommer les colonnes impliquées

Code spaghetti

Le problème est similaire à tout autre langage :

  • Code spaghetti pour le SQL
    • Écriture d'une requête à partir d'une autre requête
    • Ou évolution d'une requête au fil du temps avec des ajouts
  • Vite ingérable
    • Ne pas hésiter à reprendre la requête à zéro, en repensant sa sémantique
    • Souvent, un changement de spécification est un changement de sens, au niveau relationnel, de la requête
    • Ne pas la patcher !

Recherche textuelle

  • Objectif
    • ajouter un moteur de recherche à l'application
  • Pratique courante
    • utiliser l'opérateur LIKE
  • Problèmes
    • requiert des index spécialisés
    • recherche uniquement le terme exact
  • Solution
    • utiliser Full Text Search

Conclusion

  • Des objets de plus en plus complexes
    • mais performants
    • et remplissant un besoin
  • Des conseils
    • pour améliorer les performances
    • et pour éviter les pièges

Questions

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

Travaux Pratiques

Comprendre EXPLAIN

PostgreSQL

PostgreSQL

Introduction

  • Le matériel, le système et la configuration sont importants pour les performances
  • Mais il est aussi essentiel de se préoccuper des requêtes et de leurs performances

Au menu

  • Exécution globale d'une requête
  • Planificateur : utilité, statistiques et configuration
  • EXPLAIN
  • Nœuds d'un plan
  • Outils

Exécution globale d'une requête

  • L'exécution peut se voir sur deux niveaux
    • Niveau système
    • Niveau SGBD
  • De toute façon, composée de plusieurs étapes

Niveau système

  • Le client envoie une requête au serveur de bases de données
  • Le serveur l'exécute
  • Puis il renvoie le résultat au client

Niveau SGBD

Traitement d'une requêteSQL

Exceptions

  • Requêtes DDL
  • Instructions TRUNCATE et COPY
  • Pas de réécriture, pas de plans d'exécution... une exécution directe

Quelques définitions

  • Prédicat
    • filtre de la clause WHERE
  • Sélectivité
    • pourcentage de lignes retournées après application d'un prédicat
  • Cardinalité
    • nombre de lignes d'une table
    • nombre de lignes retournées après filtrage

Requête étudiée

Cette requête d'exemple :

SELECT matricule, nom, prenom, nom_service, fonction, localisation
  FROM employes emp
  JOIN services ser ON (emp.num_service = ser.num_service)
 WHERE ser.localisation = 'Nantes';

Plan de la requête étudiée

L'objet de ce module est de comprendre son plan d'exécution :

 Hash Join  (cost=1.06..2.29 rows=4 width=48)
   Hash Cond: (emp.num_service = ser.num_service)
   ->  Seq Scan on employes emp  (cost=0.00..1.14 rows=14 width=35)
   ->  Hash  (cost=1.05..1.05 rows=1 width=21)
         ->  Seq Scan on services ser  (cost=0.00..1.05 rows=1 width=21)
               Filter: ((localisation)::text = 'Nantes'::text)

Planificateur

  • Chargé de sélectionner le meilleur plan d'exécution
  • Énumère tous les plans d'exécution
    • Tous ou presque...
  • Calcule leur coût suivant des statistiques, un peu de configuration et beaucoup de règles
  • Sélectionne le meilleur (le moins coûteux)

Utilité

  • SQL est un langage déclaratif
  • Une requête décrit le résultat à obtenir
    • Mais pas la façon de l'obtenir
  • C'est au planificateur de déduire le moyen de parvenir au résultat demandé

Règles

  • 1ère règle : Récupérer le bon résultat
  • 2è règle : Le plus rapidement possible
    • En minimisant les opérations disques
    • En préférant les lectures séquentielles
    • En minimisant la charge CPU
    • En minimisant l'utilisation de la mémoire

Outils de l'optimiseur

  • L'optimiseur s'appuie sur :
    • un mécanisme de calcul de coûts
    • des statistiques sur les données
    • le schéma de la base de données

Optimisations

  • À partir du modèle de données
    • suppression de jointures externes inutiles
  • Transformation des sous-requêtes
    • certaines sous-requêtes transformées en jointures
  • Appliquer les prédicats le plus tôt possible
    • réduit le jeu de données manipulé
  • Intègre le code des fonctions SQL simples (inline)
    • évite un appel de fonction coûteux

Décisions

  • Stratégie d'accès aux lignes
    • Par parcours d'une table, d'un index, de TID, etc
  • Stratégie d'utilisation des jointures
    • Ordre des jointures
    • Type de jointure (Nested Loop, Merge/Sort Join, Hash Join)
    • Ordre des tables jointes dans une même jointure
  • Stratégie d'agrégation
    • Brut, trié, haché

Mécanisme de coûts

  • Modèle basé sur les coûts
    • quantifier la charge pour répondre à une requête
  • Chaque opération a un coût :
    • lire un bloc selon sa position sur le disque
    • manipuler une ligne issue d'une lecture de table ou d'index
    • appliquer un opérateur

Coûts unitaires

  • L'optimiseur a besoin de connaître :
    • le coût relatif d'un accès séquentiel au disque.
    • le coût relatif d'un accès aléatoire au disque.
    • le coût relatif de la manipulation d'une ligne en mémoire.
    • le coût de traitement d'une donnée issue d'un index.
    • le coût d'application d'un opérateur.
    • le coût de la manipulation d'une ligne en mémoire pour un parcours parallèle parallélisé.
    • le coût de mise en place d'un parcours parallélisé.

Statistiques

  • Toutes les décisions du planificateur se basent sur les statistiques
    • Le choix du parcours
    • Comme le choix des jointures
  • Statistiques mises à jour avec ANALYZE
  • Sans bonnes statistiques, pas de bons plans

Utilisation des statistiques

  • L'optimiseur utilise les statistiques pour déterminer :
    • la cardinalité d'un filtre -> quelle stratégie d'accès
    • la cardinalité d'une jointure -> quel algorithme de jointure
    • la cardinalité d'un regroupement -> quel algorithme de regroupement

Statistiques : table et index

  • Taille
  • Cardinalité
  • Stocké dans pg_class
    • relpages et reltuples

Statistiques : mono-colonne

  • Nombre de valeurs distinctes
  • Nombre d'éléments qui n'ont pas de valeur (NULL)
  • Largeur d'une colonne
  • Distribution des données
    • tableau des valeurs les plus fréquentes
    • histogramme de répartition des valeurs

Stockage des statistiques mono-colonne

  • Les informations statistiques vont dans la table pg_statistic
    • mais elle est difficile à comprendre
    • mieux vaut utiliser la vue pg_stats
    • une table vide n'a pas de statistiques
  • Taille et cardinalité dans pg_class
    • colonnes relpages et reltuples

Vue pg_stats

  • Une ligne par colonne de chaque table
  • 3 colonnes d'identification
    • schemaname, tablename, attname
  • 8 colonnes d'informations statistiques
    • inherited, null_frac, avg_width, n_distinct
    • most_common_vals, most_common_freqs, histogram_bounds
    • most_common_elems, most_common_elem_freqs, elem_count_histogram
    • correlation

Statistiques : multi-colonnes

  • Pas par défaut
  • CREATE STATISTICS
  • Deux types de statistique
    • nombre de valeurs distinctes
    • dépendances fonctionnelles
  • À partir de la version 10

Catalogue pg_statistic_ext

  • Une ligne par objet statistique
  • 4 colonnes d'identification
    • stxrelid, stxname, stxnamespace, stxkeys
  • 1 colonne pour connaître le type de statistiques géré
    • stxkind
  • 2 colonnes d'informations statistiques
    • stxndistinct
    • stxdependencies

ANALYZE

  • Ordre SQL de calcul de statistiques
    • ANALYZE [ VERBOSE ] [ table [ ( colonne [, ...] ) ] ]
  • Sans argument : base entière
  • Avec argument : la table complète ou certaines colonnes seulement
  • Prend un échantillon de chaque table
  • Et calcule des statistiques sur cet échantillon
  • Si table vide, conservation des anciennes statistiques

Fréquence d'analyse

  • Dépend principalement de la fréquence des requêtes DML
  • Cron
    • Avec psql
    • Avec vacuumdb (option --analyze-only en 9.0)
  • Autovacuum fait du ANALYZE
    • Pas sur les tables temporaires
    • Pas assez rapidement dans certains cas

Échantillon statistique

  • Se configure dans postgresql.conf
    • default_statistics_target = 100
  • Configurable par colonne

    ALTER TABLE nom ALTER [ COLUMN ] colonne SET STATISTICS valeur;
  • Par défaut, récupère 30000 lignes au hasard
    • 300 * default_statistics_target
  • Va conserver les 100 valeurs les plus fréquentes avec leur fréquence

Qu'est-ce qu'un plan d'exécution ?

  • Plan d'exécution
    • représente les différentes opérations pour répondre à la requête
    • sous forme arborescente
    • composé des nœuds d'exécution
    • plusieurs opérations simples mises bout à bout

Nœud d'exécution

  • Nœud
    • opération simple : lectures, jointures, tris, etc.
    • unité de traitement
    • produit et consomme des données
  • Enchaînement des opérations
    • chaque nœud produit les données consommées par le nœud parent
    • nœud final retourne les données à l'utilisateur

Lecture d'un plan

Lecture d'un plan d'exécution

Options de l'EXPLAIN

  • Des options supplémentaires
    • ANALYZE
    • BUFFERS
    • COSTS
    • TIMING
    • VERBOSE
    • SUMMARY
    • FORMAT
  • Donnant des informations supplémentaires très utiles

Détecter les problèmes

  • Différence importante entre l'estimation du nombre de lignes et la réalité
  • Boucles
    • appels très nombreux dans une boucle (nested loop)
    • opération lente sur lesquels PostgreSQL boucle
  • Temps d'exécution conséquent sur une opération
  • Opérations utilisant beaucoup de blocs (option BUFFERS)

Statistiques et coûts

  • Détermine à partir des statistiques
    • cardinalité des prédicats
    • cardinalité des jointures
  • Coût d'accès déterminé selon
    • des cardinalités
    • volumétrie des tables

Nœuds d'exécution les plus courants

  • Un plan est composé de nœuds
    • certains produisent des données
    • d'autres consomment des données et les retournent
    • le nœud final retourne les données à l'utilisateur
    • chaque nœud consomme au fur et à mesure les données produites par les nœuds parents

Noeuds de type parcours

  • Seq Scan
  • Parallel Seq Scan
  • Function Scan
  • et des parcours d'index

Parcours d'index

  • Index Scan
  • Index Only Scan
  • Bitmap Index Scan
  • Et leurs versions parallélisées

Noeuds de jointure

  • PostgreSQL implémente les 3 algorithmes de jointures habituels :
    • Nested Loop (boucle imbriquée)
    • Hash Join (hachage de la table interne)
    • Merge Join (tri-fusion)
  • Parallélisation possible
    • version 9.6 pour Nested Loop et Hash Join
    • version 10 pour Merge Join
  • Et pour EXISTS, IN et certaines jointures externes :
    • Semi Join et Anti Join

PostgreSQL dispose de la parallélisation depuis la version 9.6. Cela ne concernait que les jointures de type Nested Loop et Hash Join. Quant au Merge Join, il a fallu attendre la version 10 pour que la parallélisation soit supportée.

Noeuds de tris et de regroupements

  • Un seul noeud de tri :
    • Sort
  • Regroupement/Agrégation :
    • Aggregate
    • HashAggregate
    • GroupAggregate
    • Partial Aggregate/Finalize Aggregate

Les autres noeuds

  • Limit
  • Unique
  • Append (UNION ALL), Except, Intersect
  • Gather
  • InitPlan, Subplan, etc.

Problèmes les plus courants

  • L'optimiseur se trompe parfois
    • mauvaises statistiques
    • écriture particulière de la requête
    • problèmes connus de l'optimiseur

Colonnes corrélées

SELECT * FROM t1 WHERE c1=1 AND c2=1
  • c1=1 est vrai pour 20% des lignes
  • c2=1 est vrai pour 10% des lignes
  • Le planificateur va penser que le résultat complet ne récupérera que 20% * 10% (soit 2%) des lignes
    • En réalité, ça peut aller de 0 à 10% des lignes
  • Problème corrigé en version 10
    • CREATE STATISTICS pour des statistiques multi-colonnes

Mauvaise écriture de prédicats

SELECT *
FROM commandes
WHERE extract('year' from date_commande) = 2014;
  • L'optimiseur n'a pas de statistiques sur le résultat de la fonction extract
    • il estime la sélectivité du prédicat à 0.5%.

Problème avec LIKE

SELECT * FROM t1 WHERE c2 LIKE 'x%';
  • PostgreSQL peut utiliser un index dans ce cas
  • Si l'encodage n'est pas C, il faut déclarer l'index avec une classe d'opérateur
    • varchar_pattern_ops, text_pattern_ops, etc
  • En 9.1, il faut aussi faire attention au collationnement
  • Ne pas oublier pg_trgm (surtout en 9.1) et FTS

Problèmes avec LIMIT

  • Exemple
    • EXPLAIN avec LIMIT 199
    • EXPLAIN avec LIMIT 200
  • Corrigé en 9.2

DELETE lent

  • DELETE lent
  • Généralement un problème de clé étrangère
Delete  (actual time=111.251..111.251 rows=0 loops=1)
  ->  Hash Join  (actual time=1.094..21.402 rows=9347 loops=1)
        ->  Seq Scan on lot_a30_descr_lot
            (actual time=0.007..11.248 rows=34934 loops=1)
        ->  Hash  (actual time=0.501..0.501 rows=561 loops=1)
              ->  Bitmap Heap Scan on lot_a10_pdl
                  (actual time=0.121..0.326 rows=561 loops=1)
                    Recheck Cond: (id_fantoir_commune = 320013)
                    ->  Bitmap Index Scan on...
                        (actual time=0.101..0.101 rows=561 loops=1)
                          Index Cond: (id_fantoir_commune = 320013)
Trigger for constraint fk_lotlocal_lota30descrlot:
  time=1010.358 calls=9347
Trigger for constraint fk_nonbatia21descrsuf_lota30descrlot:
  time=2311695.025 calls=9347
Total runtime: 2312835.032 ms

Dédoublonnage

SELECT DISTINCT t1.* FROM t1 JOIN t2 ON (t1.id=t2.t1_id);
  • DISTINCT est souvent utilisé pour dédoublonner les lignes de t1
    • mais génère un tri qui pénalise les performances
  • GROUP BY est plus rapide
  • Une clé primaire permet de dédoublonner efficacement des lignes
    • à utiliser avec GROUP BY

Index inutilisés

  • Trop de lignes retournées
  • Prédicat incluant une transformation :

    WHERE col1 + 2 > 5
  • Statistiques pas à jour ou peu précises
  • Opérateur non-supporté par l'index :

    WHERE col1 <> 'valeur';
  • Paramétrage de PostgreSQL : effective_cache_size

Écriture du SQL

  • NOT IN avec une sous-requête
    • à remplacer par NOT EXISTS
  • Utilisation systématique de UNION au lieu de UNION ALL
    • entraîne un tri systématique
  • Sous-requête dans le SELECT
    • utiliser LATERAL

Outils

  • pgAdmin3
  • explain.depesz.com
  • pev
  • auto_explain
  • plantuner

pgAdmin3

  • Vision graphique d'un EXPLAIN
  • Une icône par nœud
  • La taille des flèches dépend de la quantité de données
  • Le détail de chaque nœud est affiché en survolant les nœuds

pgAdmin3 - copie d'écran

EXPLAIN par pgAdmin

Site explain.depesz.com

  • Site web proposant un affichage particulier du EXPLAIN ANALYZE
  • Il ne travaille que sur les informations réelles
  • Les lignes sont colorées pour indiquer les problèmes
    • Blanc, tout va bien
    • Jaune, inquiétant
    • Marron, plus inquiétant
    • Rouge, très inquiétant
  • Installable en local

explain.depesz.com - copie d'écran

explain.depesz.com

Site pev

  • Site web proposant un affichage particulier du EXPLAIN ANALYZE
    • mais différent de celui de Depesz
  • Fournir un plan d'exécution en JSON
  • Installable en local

pev - copie d'écran

EXPLAIN par pev

Extension auto_explain

  • Extension pour PostgreSQL >= 8.4
  • Connaître les requêtes lentes est bien
  • Mais difficile de connaître leur plan d'exécution au moment où elles ont été lentes
  • D'où le module auto_explain

Extension plantuner

  • Extension, pour PostgreSQL >= 8.4
  • Suivant la configuration
    • Interdit l'utilisation de certains index
    • Force à zéro les statistiques d'une table vide

Conclusion

  • Planificateur très avancé
  • Mais faillible
  • Cependant
    • ne pensez pas être plus intelligent que le planificateur

Questions

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

Annexe : Nœuds d'un plan

  • Quatre types de nœuds
    • Parcours (de table, d'index, de TID, etc.)
    • Jointures (Nested Loop, Sort/Merge Join, Hash Join)
    • Opérateurs sur des ensembles (Append, Except, Intersect, etc.)
    • Et quelques autres (Sort, Aggregate, Unique, Limit, Materialize)

Parcours

  • Ne prend rien en entrée
  • Mais renvoie un ensemble de données
    • Trié ou non, filtré ou non
  • Exemples typiques
    • Parcours séquentiel d'une table, avec ou sans filtrage des enregistrements produits
    • Parcours par un index, avec ou sans filtrage supplémentaire

Parcours de table

  • Parcours séquentiel de la table (Sequential Scan, ou SeqScan)
  • Aussi appelé FULL TABLE SCAN par d'autres SGBD
  • La table est lue entièrement
    • Même si seulement quelques lignes satisfont la requête
    • Sauf dans le cas de la clause LIMIT sans ORDER BY
  • Elle est lue séquentiellement par bloc de 8 Ko
  • Optimisation synchronize_seqscans

Parcours d'index

  • Parcours aléatoire de l'index
  • Pour chaque enregistrement correspondant à la recherche
    • Parcours non séquentiel de la table (pour vérifier la visibilité de la ligne)
  • Sur d'autres SGBD, cela revient à un
    • INDEX RANGE SCAN, suivi d'un TABLE ACCESS BY INDEX ROWID
  • Gros gain en performance quand le filtre est très sélectif
  • L'ensemble de lignes renvoyé est trié

Parcours d'index bitmap

  • En VO, Bitmap Index Scan / Bitmap Heap Scan
  • Disponible à partir de la 8.1
  • Diminuer les déplacements de la tête de lecture en découplant le parcours de l'index du parcours de la table
    • Lecture en un bloc de l'index
    • Lecture en un bloc de la partie intéressante de la table
  • Autre intérêt : pouvoir combiner plusieurs index en mémoire
    • Nœud BitmapAnd
    • Nœud BitmapOr
  • Coût de démarrage généralement important
    • Parcours moins intéressant avec une clause LIMIT

Parcours d'index seul

  • Avant la 9.2, pour une requête de ce type
    • SELECT c1 FROM t1 WHERE c1<10
  • PostgreSQL devait lire l'index et la table
    • car les informations de visibilité ne se trouvent que dans la table
  • En 9.2, le planificateur peut utiliser la « Visibility Map »
    • nouveau nœud « Index Only Scan »
    • Index B-Tree (9.2+)
    • Index SP-GiST (9.2+)
    • Index GiST (9.5+) => Types : point, box, inet, range

Parcours : autres

  • TID Scan
  • Function Scan
  • Values
  • Result

Jointures

  • Prend deux ensembles de données en entrée
    • L'une est appelée inner (interne)
    • L'autre est appelée outer (externe)
  • Et renvoie un seul ensemble de données
  • Exemples typiques
    • Nested Loop, Merge Join, Hash Join

Nested Loop

  • Pour chaque ligne de la relation externe
    • Pour chaque ligne de la relation interne
    • Si la condition de jointure est avérée
      • Émettre la ligne en résultat
  • L'ensemble externe n'est parcouru qu'une fois
  • L'ensemble interne est parcouru pour chaque ligne de l'ensemble externe
    • Avoir un index utilisable sur l'ensemble interne augmente fortement les performances

Merge Join

  • Trier l'ensemble interne
  • Trier l'ensemble externe
  • Tant qu'il reste des lignes dans un des ensembles
    • Lire les deux ensembles en parallèle
    • Lorsque la condition de jointure est avérée
    • Émettre la ligne en résultat
  • Parcourir les deux ensembles triés (d'où Sort-Merge Join)
  • Ne gère que les conditions avec égalité
  • Produit un ensemble résultat trié
  • Le plus rapide sur de gros ensembles de données

Hash Join

  • Calculer le hachage de chaque ligne de l'ensemble interne
  • Tant qu'il reste des lignes dans l'ensemble externe
    • Hacher la ligne lue
    • Comparer ce hachage aux lignes hachées de l'ensemble interne
    • Si une correspondance est trouvée
    • Émettre la ligne trouvée en résultat
  • Ne gère que les conditions avec égalité
  • Idéal pour joindre une grande table à une petite table
  • Coût de démarrage important à cause du hachage de la table

Suppression d'une jointure

SELECT pg_class.relname, pg_class.reltuples
FROM pg_class
LEFT JOIN pg_namespace
       ON pg_class.relnamespace=pg_namespace.oid;
  • Un index unique existe sur la colonne oid de pg_namespace
  • Jointure inutile
    • sa présence ne change pas le résultat
  • PostgreSQL peut supprimer la jointure à partir de la 9.0

Ordre de jointure

  • Trouver le bon ordre de jointure est un point clé dans la recherche de performances
  • Nombre de possibilités en augmentation factorielle avec le nombre de tables
  • Si petit nombre, recherche exhaustive
  • Sinon, utilisation d'heuristiques et de GEQO
    • Limite le temps de planification et l'utilisation de mémoire
    • GEQO remplacé par Simulated Annealing ? (recuit simulé en VF)

Opérations ensemblistes

  • Prend un ou plusieurs ensembles de données en entrée
  • Et renvoie un ensemble de données
  • Concernent principalement les requêtes sur des tables partitionnées ou héritées
  • Exemples typiques
    • Append
    • Intersect
    • Except

Append

  • Prend plusieurs ensembles de données
  • Fournit un ensemble de données en sortie
    • Non trié
  • Utilisé par les requêtes
    • Sur des tables héritées (partitionnement inclus)
    • Ayant des UNION ALL et des UNION
    • Attention que le UNION sans ALL élimine les duplicats, ce qui nécessite une opération supplémentaire de tri

MergeAppend

  • Append avec optimisation
  • Fournit un ensemble de données en sortie trié
  • Utilisé par les requêtes
    • UNION ALL ou partitionnement/héritage
    • Utilisant des parcours triés
    • Idéal avec Limit

Autres

  • Nœud HashSetOp Except
    • instructions EXCEPT et EXCEPT ALL
  • Nœud HashSetOp Intersect
    • instructions INTERSECT et INTERSECT ALL

Divers

  • Prend un ensemble de données en entrée
  • Et renvoie un ensemble de données
  • Exemples typiques
    • Sort
    • Aggregate
    • Unique
    • Limit
    • InitPlan, SubPlan

Sort

  • Utilisé pour le ORDER BY
    • Mais aussi DISTINCT, GROUP BY, UNION
    • Les jointures de type Merge Join
  • Gros délai de démarrage
  • Trois types de tri
    • En mémoire, tri quicksort
    • En mémoire, tri top-N heapsort (si clause LIMIT)
    • Sur disque

Aggregate

  • Agrégat complet
  • Pour un seul résultat

Hash Aggregate

  • Hachage de chaque n-uplet de regroupement (group by)
  • accès direct à chaque n-uplet pour appliquer fonction d’agrégat
  • Intéressant si l'ensemble des valeurs distinctes tient en mémoire, dangereux sinon

Group Aggregate

  • Reçoit des données déjà triées
  • Parcours des données
    • Regroupement du groupe précédent arrivé à une donnée différente

Unique

  • Reçoit des données déjà triées
  • Parcours des données
    • Renvoi de la donnée précédente une fois arrivé à une donnée différente
  • Résultat trié

Limit

  • Permet de limiter le nombre de résultats renvoyés
  • Utilisé par
    • clauses LIMIT et OFFSET d'une requête SELECT
    • fonctions min() et max() quand il n'y a pas de clause WHERE et qu'il y a un index
  • Le nœud précédent sera de préférence un nœud dont le coût de démarrage est peu élevé (SeqScan, NestedLoop)

Travaux Pratiques