SQL Avancé

Formation SQL2

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 :

  • PaternitĂ©

  • Pas d'utilisation commerciale

  • Partage des conditions initiales Ă  l'identique

PostgreSQL : Optimisations SQL

PostgreSQL

Introduction

  • L'optimisation doit porter sur les diffĂ©rents composants
    • Le serveur qui hĂ©berge le SGBDR : le matĂ©riel, la distribution et le kernel
    • Le moteur de la base de donnĂ©es : postgresql.conf
    • La base de donnĂ©es : l'organisation des fichiers de PostgreSQL
    • L'application en elle-mĂŞme : le schĂ©ma, les requĂŞtes et tout ce qui s'y rapporte
  • Ce module se focalise sur ce dernier point

Axes d'optimisation

  • Il est illusoire d'essayer d'optimiser une application sans dĂ©terminer au prĂ©alable les sources de ralentissement
  • Loi de Pareto : « Le pourcentage de la population dont la richesse est supĂ©rieure Ă  une valeur x est proportionnel Ă  A/x^α » (Vilfredo Pareto, Ă©conomiste du XIXe siècle)
  • Principe de Pareto (dĂ©rivĂ©) : 80% des effets sont produits par 20% des causes.

  • L'optimisation doit donc ĂŞtre ciblĂ©e :
    • il s'agit de trouver ces « 20% » de causes.

Recherche des axes d'optimisation

  • Utilisation de profiler
    • PostgreSQL : pgBadger, PoWA, pg_stat_statements, pg_stat_plans
    • Oracle : tkprof, statspack, AWR…
    • SQL Server : SQL Server Profiler

Problématiques liées au schéma

  • PostgreSQL est un SGBD-R, un système de gestion de bases de donnĂ©es relationnel
  • Le schĂ©ma est d'une importance capitale
  • « Relationnel » n'est pas « relation entre tables »
  • Les tables SONT les relations (entre attributs)

Quelques rappels sur le modèle relationnel

  • Le but est de modĂ©liser un ensemble de faits
  • Le modèle relationnel a Ă©tĂ© introduit Ă  l'Ă©poque des bases de donnĂ©es hiĂ©rarchiques
    • Pointeur : incohĂ©rence Ă  terme
    • Formalisme : relations, modĂ©lisation Ă©vitant les incohĂ©rences suite Ă  modification
    • Formes normales
  • Un modèle n'est qu'un modèle : il ne traduit pas la rĂ©alitĂ©, simplement ce qu'on souhaite en reprĂ©senter
  • Identifier rapidement les problèmes les plus Ă©vidents

Formes normales

Il existe une définition mathématique précise de chacune des 7 formes normales.

  • La troisième forme normale peut toujours ĂŞtre atteinte
  • La forme suivante (forme normale de Boyce-Codd, ou FNBC) ne peut pas toujours ĂŞtre atteinte
  • La cible est donc habituellement la 3FN
  • DĂ©finition simple par Chris Date :
    • « Chaque attribut dĂ©pend de la clĂ©, de TOUTE la clĂ©, et QUE de la clĂ© »
    • « The key, the whole key, nothing but the key »

Atomicité

  • Un attribut (colonne) doit ĂŞtre atomique :

    • Modifier l'attribut sans en toucher un autre
    • DonnĂ©e correcte : boolean abs, boolean volant_a_gauche, enum couleur, etc. Difficile
    • Recherche efficace : accĂ©dĂ© en entier dans une clause WHERE
    • Non respect = violation de la première forme normale

Atomicité - mauvais exemple

Immatriculation Modèle Caractéristiques

TT-802-AX

Clio

4 roues motrices, ABS, toit ouvrant, peinture verte

QS-123-DB

AX

jantes aluminium, peinture bleu

INSERT INTO voiture
VALUES ('AD-057-GD','Clio','interieur bleu, anti-blocage des roues');

Atomicité - proposition

    Column      |  Type   |            Description             
----------------+---------+------------------------------------
immatriculation | text    | 
modele          | text    | 
couleur         | color   | Couleur vehicule (bleu,rouge,vert)
toit_ouvrant    | boolean | Option toit ouvrant
abs             | boolean | Option anti-blocage des roues
type_roue       | boolean | tole/aluminium
motricite       | boolean | 2 roues motrices / 4 roues motrices

NULL

NULL signifie habituellement :

  • Valeur non renseignĂ©e
  • Valeur inconnue

Dans tous les cas, c'est une absence d'information. Ou du moins la seule information est qu'on ne sait pas.

Une table remplie de NULLs est habituellement signe d'un problème de modélisation.

Colonne de type variable

Plus rarement, on rencontre aussi :

  • Une colonne de type varchar
  • qui contient :
    • quelquefois un entier
    • quelquefois une date
    • un NULL
    • une chaĂ®ne autre
    • etc.
  • Ă€ Ă©viter comme la peste !

Stockage Entité-Clé-Valeur

  • EntitĂ©-Attribut-Valeur (ou EntitĂ©-ClĂ©-Valeur)
  • Quel but ?
    • 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)

Stockage Entité-Clé-Valeur - exemple

id_pers nom_attr val_attr

1

nom

Prunelle

1

prenom

LĂ©on

1

telephone

0123456789

1

fonction

dba

Comment lister tous les dba?

Stockage Entité-Clé-Valeur - requête associée...

SELECT id, att_nom.val_attr nom , att_prenom.val_attr prenom,att_telephone.val_attr tel
FROM personnes p
JOIN personne_attributs att_nom 
 ON (p.id=att_nom.id_pers AND att_nom.nom_attr='nom')
JOIN personne_attributs att_prenom
 ON (p.id=att_prenom.id_pers AND att_prenom.nom_attr='prenom')
JOIN personne_attributs att_telephone
 ON (p.id=att_telephone.id_pers AND att_telephone.nom_attr='telephone')
JOIN personne_attributs att_fonction
 ON (p.id=att_fonction.id_pers AND att_fonction.nom_attr='fonction')
WHERE att_fonction.val_attr='dba';

Nombreuses colonnes

Tables Ă  plusieurs dizaines, voire centaines de colonnes :

  • Les entitĂ©s sont certainement trop grosses dans la modĂ©lisation
  • Il y a probablement dĂ©pendance entre certaines colonnes (« Only the key »)
  • On accède Ă  beaucoup d'attributs inutiles (tout est stockĂ© au mĂŞme endroit)

Absence de contraintes

  • Parfois (souvent ?) ignorĂ©es pour diverses raisons :
    • faux gains de performance
    • flexibilitĂ© du modèle de donnĂ©es
    • compatibilitĂ© avec d'autres SGBD
    • commoditĂ© de dĂ©veloppement
  • Les contraintes sont utiles Ă  l'optimiseur :
    • dĂ©terminent l'unicitĂ© des valeurs
    • Ă©radiquent des lectures de tables inutiles sur des LEFT JOIN
    • utilisent les contraintes CHECK pour exclure une partition

Complexité

Pour les performances, on envisage souvent de distribuer la base sur plusieurs nœuds.

  • La complexitĂ© augmente (au niveau du code applicatif et/ou des procĂ©dures d'exploitation)
    • le risque d'erreur avec lui (programmation, fausse manipulation)
  • Le retour Ă  un Ă©tat stable après un incident est complexe

SQL - RequĂŞtes

  • Le SQL est un langage dĂ©claratif :
    • on dĂ©crit le rĂ©sultat et pas la façon de l'obtenir
    • comme Prolog
    • c'est le travail de la base de dĂ©terminer le traitement Ă  effectuer
  • Traitement ensembliste :
    • par opposition au traitement procĂ©dural
    • « on effectue des opĂ©rations sur des relations pour obtenir des relations »

Opérateurs relationnels

Les opérateurs purement relationnels sont les suivants :

  • Projection
    • Clause SELECT (choix des colonnes)
  • SĂ©lection
    • Clause WHERE (choix des enregistrements)
  • Jointure
    • Clause FROM/JOIN (choix des tables)
  • Bref, tout ce qui dĂ©termine sur quelles donnĂ©es on travaille

Opérateurs non-relationnels

  • Les autres opĂ©rateurs sont non-relationnels :
    • ORDER BY
    • GROUP BY/DISTINCT
    • HAVING
    • Sous-requĂŞte, vue
    • Fonction (classique, d'agrĂ©gat, analytique)
    • Jointure externe

Données utiles

Le volume de données récupéré a un impact sur les performances.

  • N'accĂ©der qu'aux tables nĂ©cessaires
  • N'accĂ©der qu'aux colonnes nĂ©cessaires
  • Plus le volume de donnĂ©es Ă  traiter est Ă©levĂ©, plus les opĂ©rations seront lentes :
    • Tris et Jointures
    • Éventuellement stockage temporaire sur disque pour certains algorithmes

Limiter le nombre de requĂŞtes

SQL : langage ensembliste et déclaratif

  • Ne pas faire de traitement unitaire par enregistrement
  • Utiliser les jointures, ne pas accĂ©der Ă  chaque table une-par-une
  • Une seule requĂŞte, parcours de curseur
  • FrĂ©quent avec les ORM

Éviter les vues non-relationnelles

Une vue est simplement une requête pré-déclarée en base.

  • C'est l'Ă©quivalent relationnel d'une fonction
  • Quand elle est utilisĂ©e dans une autre requĂŞte, elle est initialement traitĂ©e comme une sous-requĂŞte
  • Attention aux vues avec DISTINCT, GROUP BY etc.
    • Impossible de l'inliner
    • Barrière d'optimisation
    • Donc mauvaises performances
  • Les vues sont dangereuses en termes de performance
    • masquent la complexitĂ©

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
    • É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
    • Un changement de spĂ©cification est un changement de sens, au niveau relationnel, de la requĂŞte
    • Ne pas la patcher !

Sous-requĂŞtes 1/2

  • Si IN, limiter le nombre d'enregistrements grâce Ă  DISTINCT

    SELECT * FROM t1
      WHERE val IN (SELECT DISTINCT …)
  • Éviter une requĂŞte liĂ©e :

    SELECT a,b
      FROM t1
      WHERE val IN (SELECT f(b))

Sous-requĂŞtes 2/2

  • Certaines sous-requĂŞtes sont l'expression de Semi-join ou Anti-join

    SELECT * FROM t1 WHERE fk
       [NOT] IN (SELECT pk FROM t2 WHERE xxx)
    SELECT * FROM t1 WHERE [NOT] EXISTS
       (SELECT 1 FROM t2 WHERE t2.pk=t1.fk AND xxx)
    SELECT t1.* FROM t1 LEFT JOIN t2 ON (t1.fk=t2.pk)
       WHERE t2.id IS [NOT] NULL`
  • sont strictement Ă©quivalentes !
    • L'optimiseur les exĂ©cute Ă  l'identique (sauf NOT IN)

Accès aux données

L'accès aux données est coûteux.

  • Quelle que soit la base
  • Dialogue entre client et serveur
    • Plusieurs aller/retours potentiellement
  • Analyse d'un langage complexe
    • SQL PostgreSQL : gram.y de 14000 lignes
  • Calcul de plan :
    • langage dĂ©claratif => converti en impĂ©ratif Ă  chaque exĂ©cution

Maintien des connexions

Se connecter coûte cher :

  • VĂ©rification authentification, permissions
  • CrĂ©ation de processus, de contexte d'exĂ©cution
  • Éventuellement nĂ©gotiation SSL
  • Acquisition de verrous

=> Maintenir les connexions coté applicatif ou utiliser un pooler.

Penser relationnel

Les spécifications sont souvent procédurales, voire objet !

  • Il faut prendre du recul, et rĂ©flĂ©chir de façon ensembliste
    • On travaille sur des ensembles de donnĂ©es
    • On peut faire encore mieux avec les CTE (SQL:1999)

Pas de DDL applicatif

  • Le schĂ©ma reprĂ©sente la modĂ©lisation des donnĂ©es
    • Une application n'a pas Ă  y toucher lors de son fonctionnement normal
    • Parfois : tables temporaires locales Ă  des sessions
    • Toujours voir si une autre solution est possible
  • SQL manipule les donnĂ©es en flux continu :
    • chaque Ă©tape d'un plan d'exĂ©cution n'attend pas la fin de la prĂ©cĂ©dente
    • Passer par une table temporaire est probablement une perte de temps

Optimiser chaque accès

Un ordre SQL peut effectuer de nombreuses choses :

  • Les moteurs SQL sont très efficaces, et Ă©voluent en permanence
  • Ils ont de nombreuses mĂ©thodes de tri, de jointure, qu'ils choisissent en fonction du contexte
  • Si vous utilisez le langage SQL, votre requĂŞte profitera des futures Ă©volutions
  • Si vous codez tout dans votre programme, vous devrez le maintenir et l'amĂ©liorer
  • Faites un maximum du cĂ´tĂ© SQL : agrĂ©gats, fonctions analytiques, tris, numĂ©rotations, CASE, etc.
  • Commentez votre code avec -- et /* */

Ne faire que le nécessaire

Encore une fois, prendre de la distance vis-à-vis des spécifications fonctionnelles :

  • Si le client existe, le mettre Ă  jour :
    • Le mettre Ă  jour, et regarder combien d'enregistrements ont Ă©tĂ© mis Ă  jour
  • Si le client existe :
    • Surtout pas de COUNT(*), Ă©ventuellement un test de l'existence d'UN enregistrement
  • GĂ©rer les exceptions plutĂ´t que de vĂ©rifier prĂ©alablement que les conditions sont remplies (si l'exception est rare)

Index

  • La bonne utilisation d'un index est un sujet Ă  part entière :
    • sujet effleurĂ© ici
    • excellent livre par Markus Winand : SQL Performance Explained
  • Compromis insertion/sĂ©lection
  • Objet technique :
    • ni dans la thĂ©orie relationnelle
    • ni dans la norme SQL

Utilité d'un index

  • Un index permet de :
    • trouver un enregistrement dans une table directement
    • rĂ©cupĂ©rer une sĂ©rie d'enregistrements dans une table
    • voire rĂ©cupĂ©rer directement l'enregistrement de l'index s'il contient toutes les colonnes nĂ©cessaires
  • En complĂ©ment, un index facilite :
    • certains tris
    • certains agrĂ©gats
  • Et est utilisĂ© pour les contraintes d'unicitĂ© !

Index et SELECT

Un index améliore les SELECT

Sans index :

=# SELECT * FROM t1 WHERE i = 10000;
Temps : 1760,017 ms

Avec index :

=# CREATE INDEX idx_t1_i ON t1 (i);
=# SELECT * FROM t1 WHERE i = 10000;
Temps : 27,711 ms

Index et INSERT

La présence d'un index ralentit les mises à jour :

=# INSERT INTO t1 SELECT i FROM generate_series(1, 10000000) i;
Temps : 39674,079 ms
=# CREATE INDEX idx_t1_i ON t1 (i);
=# INSERT INTO t1 SELECT i FROM generate_series(1, 10000000) i;
Temps : 94925,140 ms

Compromis Ă  trouver :

  • favoriser les lectures
  • mais pas au dĂ©triment des Ă©critures

Quelles requĂŞtes optimiser ?

  • Seules un certain nombre de requĂŞtes sont critiques
    • utilisation d'outil de profiling pour les identifier
    • le travail d'optimisation se porte sur celles-ci uniquement
  • DĂ©termination des requĂŞtes critiques :
    • longues en temps cumulĂ©, coĂ»teuses en ressources serveur
    • longues et interactives, mauvais ressenti des utilisateurs

Index spécialisés

  • PostgreSQL dispose d'index spĂ©cialisĂ©s :
    • GIN (Generalized Inverted Index)
    • GiST (Generalized Search Tree)
    • BRIN (Block Range INdex)

Index GIN et GiST

  • Ils permettent d'indexer des donnĂ©es non-scalaires :
    • Intervalles de dates
    • formes gĂ©omĂ©triques, donnĂ©es gĂ©ographiques
    • Trigrammes, Full Text
    • Tableaux

Index BRIN

  • Les index BRIN sont utiles pour les grosses volumĂ©tries
  • Les donnĂ©es sont corrĂ©lĂ©es avec leur emplacement physique

Index fonctionnels

  • Si une fonction est appliquĂ©e Ă  une colonne dans un prĂ©dicat :

    SELECT ... FROM table WHERE f(colonne)=C
    • l'optimiseur n'utilise pas d'index
    • dĂ©note un problème probable de normalisation
  • Analogie : chercher dans un dictionnaire français
    • WHERE anglais(mot)='cheval' => il faut traduire chaque mot lu

Index fonctionnels

Usage classique :

  • Recherche sans la casse
  • Avec un index fonctionnel, l'optimiseur sait utiliser un index :

    CREATE INDEX index ON dictionnaire_fr(anglais(mot))

Impact des transactions

  • Prise de verrous : ils ne sont relâchĂ©s qu'Ă  la fin
    • COMMIT
    • ROLLBACK
  • Validation des donnĂ©es sur le disque au COMMIT
    • Écriture synchrone : coĂ»teux
  • Faire des transactions qui correspondent au fonctionnel
  • Si traitement lourd, prĂ©fĂ©rer des transactions de grande taille

Verrouillage et contention

  • Chaque transaction prend des verrous :
    • sur les objets (tables, index, etc.) pour empĂŞcher au moins leur suppression ou modification de structure pendant leur travail
    • sur les enregistrements
    • libĂ©rĂ©s Ă  la fin de la transaction : les transactions très longues peuvent donc ĂŞtre problĂ©matiques
  • Sous PostgreSQL, on peut quand mĂŞme lire un enregistrement en cours de modification : on voit l'ancienne version (MVCC)

Deadlocks

  • Du fait de ces verrous :
    • On peut avoir des deadlocks (verrous mortels)
    • En thĂ©orie, on peut les Ă©viter (en prenant toujours les verrous dans le mĂŞme ordre)
    • En pratique, ça n'est pas toujours possible ou commode
    • Les SGBD tuent une des transactions responsables du deadlock
    • Une application gĂ©nĂ©rant de nombreux deadlocks est ralentie

Bibliographie

  • Ce document s'appuie sur de nombreuses sources.

  • Si vous souhaitez approfondir les points abordĂ©s :
    • The World and the Machine, Michael Jackson
    • The Art of SQL, StĂ©phane Faroult
    • Refactoring SQL Applications, StĂ©phane Faroult
    • SQL Performance Explained, Markus Winand
    • Introduction aux bases de donnĂ©es, Chris Date
    • VidĂ©os de StĂ©phane Faroult (roughsealtd) sous Youtube

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

Techniques d'indexation

Techniques d'indexation

Introduction

  • Qu'est-ce qu'un index ?
  • Comment indexer une base ?
  • Les diffĂ©rents types d'index

Au menu

  • Anatomie d'un index
  • Les index « simples »
  • MĂ©thodologie
  • Indexation avancĂ©e
  • Outillage

Objectifs

  • Comprendre ce qu'est un index
  • MaĂ®triser le processus de crĂ©ation d'index
  • ConnaĂ®tre les diffĂ©rents types d'index et leurs cas d'usages

Fonctionnement d'un index

  • Analogie : index dans une publication scientifique
    • Structure sĂ©parĂ©e, associant des clĂ©s (termes) Ă  des localisations (pages)
    • MĂŞme principe pour un index dans un SGBD
  • Structure de donnĂ©es spĂ©cialisĂ©e, plusieurs types
  • Existe en dehors de la table

Un index n'est pas magique…

  • Un index ne rĂ©sout pas tout
  • Importance de la conception du schĂ©ma de donnĂ©es
  • Importance de l'Ă©criture de requĂŞtes SQL correctes

Index btree

  • Type d'index le plus courant
  • Mais aussi le plus simple
  • UtilisĂ© pour les contraintes d'unicitĂ©
  • Supporte les opĂ©rateurs suivants : <, <=, =, >=, >
  • Supporte le tri
  • Ne peut pas indexer des colonnes de plus de 2.6 Ko

Concrètement…

Organisation d'un index btree

Impact sur les performances

  • L'index n'est pas gratuit !
  • Espace disque nĂ©cessaire
  • Maintenance Ă  chaque opĂ©ration DML
  • Mesurer la pertinence de l'index

Index multicolonnes

  • PossibilitĂ© d'indexer plusieurs colonnes :

    CREATE INDEX ON ma_table (id, name) ;
  • L'ordre des colonnes est primordial
    • permet de rĂ©pondre aux requĂŞtes sur les premières colonnes de l'index
    • pour les autres, PostreSQL lira tout l'index ou ignorera l'index

Méthodologie de création d'index

  • On indexe pour une requĂŞte, ou idĂ©alement une collection de requĂŞte
  • On n'indexe pas « une table »

L'index ? Quel index ?

  • Identifier les requĂŞtes nĂ©cessitant un index
  • CrĂ©er les index permettant de rĂ©pondre Ă  ces requĂŞtes
  • Valider le fonctionnement, en rejouant la requĂŞte avec :
EXPLAIN (ANALYZE, BUFFERS)

Index et clés étrangères

  • Indexation des colonnes faisant rĂ©fĂ©rence Ă  une autre
  • Performances des DML
  • Performances des jointures

Index non utilisés

  • Pas le bon type (CAST)
  • Pas les bons opĂ©rateurs
  • SĂ©lectivitĂ© trop faible
  • Index redondants

Indexation avancée

De nombreuses possibilités d'indexation avancée :

  • Index multi-colonnes
  • Index fonctionnels
  • Index partiels
  • Covering indexes
  • Classes d'opĂ©rateurs
  • GiN
  • GiST
  • BRIN
  • Hash

Index partiels

  • N'indexe qu'une partie des donnĂ©es :

    CREATE INDEX ON table (colonne) WHERE condition;
  • Ne sert que si la clause exacte est respectĂ©e !
  • IntĂ©rĂŞt : index beaucoup plus petit !

Index partiels : cas d'usage

  • DonnĂ©es chaudes et froides
  • Index pour une requĂŞte ayant une condition fixe
  • Éviter les index de type :
CREATE INDEX ON index(une_colonne) WHERE une_colonne = 'test`

Index Fonctionnels

  • Il s'agit d'un index sur le rĂ©sultat d'une fonction :

    WHERE upper(a)='DUPOND'
  • l'index classique ne fonctionne pas

    CREATE INDEX mon_idx ON ma_table ((upper(a))
  • La fonction doit ĂŞtre IMMUTABLE

Covering Indexes

On trouve parfois « index couvrants » dans la littérature française.

CREATE INDEX idx1 on T1 (col1,col2)
  • RĂ©pondent Ă  la clause WHERE
  • ET contiennent toutes les colonnes demandĂ©es par la requĂŞte :

    SELECT col1,col2` FROM t1 WHERE col1>12
  • Pas de visite de la table (donc peu d'accès alĂ©atoires, l'index Ă©tant Ă  peu près triĂ© physiquement)

Classes d'opérateurs

  • Un index utilise des opĂ©rateurs de comparaison :
  • Il peut exister plusieurs façons de comparer deux donnĂ©es du mĂŞme type
  • Par exemple, pour les chaĂ®nes de caractères
    • DiffĂ©rentes collations
    • Tri sans collation (pour LIKE)

      CREATE INDEX idx1 ON ma_table (col_varchar varchar_pattern_ops)
  • Permet :

    SELECT ... FROM ma_table WHERE col_varchar LIKE 'chaine%'

GIN

GIN : Generalized Inverted iNdex

  • Index inversĂ© gĂ©nĂ©ralisĂ©
  • Index inversĂ© ?
    • Index associe une valeur Ă  la liste de ses adresses
    • Utile pour tableaux, listes…
  • Pour chaque entrĂ©e du tableau
    • Liste d'adresses (TID) oĂą le trouver
    • CompressĂ©e Ă  partir de 9.4 => alternative Ă  bitmap

GiST

GiST : Generalized Search Tree

  • Arbre de recherche gĂ©nĂ©ralisĂ©
  • Indexation non plus des valeurs mais de la vĂ©racitĂ© de prĂ©dicats
  • Moins performants car moins sĂ©lectifs que btree
  • Mais peuvent indexer Ă  peu près n'importe quoi
  • Multi-colonnes dans n'importe quel ordre
  • Sur-ensemble de btree et rtree

KNN

  • KNN = K-Nearest neighbours, K plus proches voisins
  • RequĂŞtes de types

    ORDER BY ma_colonne <-> une_référence LIMIT 10
  • Très utile pour la recherche de mots ressemblants, gĂ©ographique

BRIN

BRIN : Block Range INdex (9.5+)

  • Utile pour les tables très volumineuses
    • L'index produit est petit
  • Performant lorsque les valeurs sont corrĂ©lĂ©es Ă  leur emplacement physique
  • Types qui peuvent ĂŞtre triĂ©s linĂ©airement (pour obtenir min/max)

Hash

Index Hash : * Journalisés uniquement depuis la version 10 * donc facilement corrompus sur les versions antérieures * Moins performants que les btree * Ne gèrent que les égalités, pas « < » et « > » * Mais plus compacts * À ne pas utiliser

Outils

  • pour l'identification des requĂŞtes
  • pour l'identification des prĂ©dicats et des requĂŞtes liĂ©es
  • pour la validation de l'index Ă  crĂ©er

Identifier les requĂŞtes

  • PgBadger
  • pg_stat_statements
  • PoWA

Identifier les prédicats et des requêtes liées

  • pg_qualstats
    • avec PoWa

Étude des index à créer

  • PoWA
  • HypoPG

Conclusion

  • ResponsabilitĂ© de l'indexation
  • ComprĂ©hension des mĂ©canismes
  • DiffĂ©rents types d'index, diffĂ©rentes stratĂ©gies
  • Outillage

Travaux Pratiques

SQL avancé pour le transactionnel

Préambule

  • SQL et PostgreSQL proposent de nombreuses possibilitĂ©s avancĂ©es
    • normes SQL:99, 2003, 2008 et 2011
    • parfois, extensions propres Ă  PostgreSQL
  • LIMIT/OFFSET
  • jointures LATERAL
  • UPSERT : INSERT ou UPDATE
  • Common Table Expressions
  • Serializable Snapshot Isolation

Objectifs

  • Aller au-delĂ  de SQL:92
  • Concevoir des requĂŞtes simples pour rĂ©soudre des problèmes complexes

LIMIT

  • Clause LIMIT
  • ou syntaxe en norme SQL : FETCH FIRST xx ROWS
  • Utilisation :
    • limite le nombre de lignes du rĂ©sultat

LIMIT : exemple

SELECT *
  FROM employes
 LIMIT 2;

 matricule |   nom    | service  | salaire
-----------+----------+----------+----------
 00000001  | Dupuis   |          | 10000.00
 00000004  | Fantasio | Courrier |  4500.00
(2 lignes)

OFFSET

  • Clause OFFSET
    • Ă  utiliser avec LIMIT
  • UtilitĂ© :
    • pagination de rĂ©sultat
    • sauter les n premières lignes avant d'afficher le rĂ©sultat

OFFSET : exemple (1/2)

  • Sans offset :
SELECT *
  FROM employes
 LIMIT 2
 ORDER BY matricule;

 matricule |   nom    | service  | salaire
-----------+----------+----------+----------
 00000001  | Dupuis   |          | 10000.00
 00000004  | Fantasio | Courrier |  4500.00

OFFSET : exemple (2/2)

  • En sautant les deux premières lignes :
SELECT *
  FROM employes
 ORDER BY matricule
 LIMIT 2
 OFFSET 2;

 matricule |   nom    |   service   | salaire
-----------+----------+-------------+---------
 00000006  | Prunelle | Publication | 4000.00
 00000020  | Lagaffe  | Courrier    | 3000.00

OFFSET : alternative

  • OFFSET peut ĂŞtre problĂ©matique :
    • beaucoup de donnĂ©es lues
    • temps de rĂ©ponse dĂ©gradĂ©s
  • Alternative possible
    • utilisation d'un index
    • couplĂ© aux donnĂ©es composites
  • Article sur le sujet

RETURNING

  • Clause RETURNING
  • UtilitĂ© :
    • rĂ©cupĂ©rer les enregistrements modifiĂ©s
    • avec INSERT
    • avec UPDATE
    • avec DELETE

RETURNING : exemple

CREATE TABLE test_returning (id serial primary key, val integer);

INSERT INTO test_returning (val)
  VALUES (10)
RETURNING id, val;

 id | val
----+-----
  1 |  10
(1 ligne)

UPSERT

  • INSERT ou UPDATE ?
    • INSERT ... ON CONFLICT DO { NOTHING | UPDATE }
    • Ă€ partir de la version 9.5
  • UtilitĂ© :
    • mettre Ă  jour en cas de conflit sur un INSERT
    • ne rien faire en cas de conflit sur un INSERT

UPSERT : problème à résoudre

  • InsĂ©rer une ligne dĂ©jĂ  existante provoque une erreur :
INSERT INTO employes (matricule, nom, service, salaire)
 VALUES ('00000001', 'Marsupilami', 'Direction', 50000.00);
ERROR:  duplicate key value violates unique constraint
        "employes_pkey"
DETAIL:  Key (matricule)=(00000001) already exists.

ON CONFLICT DO NOTHING

  • la clause ON CONFLICT DO NOTHING Ă©vite d'insĂ©rer une ligne existante :
=# INSERT INTO employes (matricule, nom, service, salaire)
   VALUES ('00000001', 'Marsupilami', 'Direction', 50000.00)
   ON CONFLICT DO NOTHING;
INSERT 0 0

ON CONFLICT DO NOTHING : syntaxe

INSERT ....
ON CONFLICT
  DO NOTHING;

ON CONFLICT DO UPDATE

INSERT INTO employes (matricule, nom, service, salaire)
VALUES ('00000001', 'M. Pirate', 'Direction', 0.00)
ON CONFLICT (matricule)
   DO UPDATE SET salaire = employes.salaire,
                 nom = excluded.nom
RETURNING *;

 matricule |    nom    |   service   | salaire
-----------+-----------+-------------+----------
 00000001  | M. Pirate | Direction   | 50000.00

ON CONFLICT DO UPDATE

  • Avec plusieurs lignes insĂ©rĂ©es :
INSERT INTO employes (matricule, nom, service, salaire)
VALUES ('00000002', 'Moizelle Jeanne', 'Publication', 3000.00),
       ('00000040', 'Lebrac', 'Publication', 3100.00)
ON CONFLICT (matricule)
   DO UPDATE SET salaire = employes.salaire,
                 nom = excluded.nom
RETURNING *;

 matricule |       nom       |   service   | salaire
-----------+-----------------+-------------+----------
 00000002  | Moizelle Jeanne | Publication |  3000.00
 00000040  | Lebrac          | Publication |  3000.00

ON CONFLICT DO UPDATE : syntaxe

  • colonne(s) portant(s) une contrainte d'unicitĂ©
  • pseudo-table excluded
INSERT ....
ON CONFLICT (<colonne clé>)
  DO UPDATE
        SET colonne_a_modifier = excluded.colonne,
            autre_colonne_a_modifier = excluded.autre_colonne,
            ...;

LATERAL

  • Jointures LATERAL
    • SQL:99
    • PostgreSQL 9.3
    • Ă©quivalent d'une boucle foreach
  • Utilisations
    • top-N Ă  partir de plusieurs tables
    • jointure avec une fonction retournant un ensemble

LATERAL : avec une sous-requĂŞte

  • Jointure LATERAL
    • Ă©quivalent de foreach
  • UtilitĂ© :
    • Top-N Ă  partir de plusieurs tables
    • exemple : afficher les 5 derniers messages des 5 derniers sujets actifs d'un forum

LATERAL : exemple

SELECT titre,
       top_5_messages.date_publication,
       top_5_messages.extrait
  FROM sujets,
  LATERAL(SELECT date_publication,
                 substr(message, 0, 100) AS extrait
          FROM messages
         WHERE sujets.sujet_id = messages.sujet_id
         ORDER BY date_publication DESC
         LIMIT 5) top_5_messages
 ORDER BY sujets.date_modification DESC,
          top_5_messages.date_publication DESC
 LIMIT 25;

LATERAL : principe

Principe LATERAL

LATERAL : avec une fonction

  • Utilisation avec une fonction retournant un ensemble
    • clause LATERAL optionnelle
  • UtilitĂ© :
    • extraire les donnĂ©es d'un tableau ou d'une structure JSON sous la forme tabulaire
    • utiliser une fonction mĂ©tier qui retourne un ensemble X selon un ensemble Y fourni

LATERAL : exemple avec une fonction

SELECT titre,
       top_5_messages.date_publication,
       top_5_messages.extrait
  FROM sujets,
       get_top_5_messages(sujet_id) AS top_5_messages
 ORDER BY sujets.date_modification DESC
 LIMIT 25;

Common Table Expressions

  • Common Table Expressions
    • clauses WITH et WITH RECURSIVE
  • UtilitĂ© :
    • factoriser des sous-requĂŞtes

CTE et SELECT

  • UtilitĂ©
    • factoriser des sous-requĂŞtes
    • amĂ©liorer la lisibilitĂ© d'une requĂŞte

CTE et SELECT : exemple

WITH resultat AS (
   /* requĂŞte complexe */
)
SELECT *
  FROM resultat
 WHERE nb < 5;

CTE et SELECT : syntaxe

WITH nom_vue1 AS (
 <requête pour générer la vue 1>
)
SELECT *
  FROM nom_vue1;

CTE et barrière d'optimisation

  • Attention, une CTE est une barrière d'optimisation
    • pas de transformations
    • pas de propagation des prĂ©dicats

CTE en Ă©criture

  • CTE avec des requĂŞtes en modification
    • avec INSERT/UPDATE/DELETE
    • et Ă©ventuellement RETURNING
    • obligatoirement exĂ©cutĂ© sur PostgreSQL
  • Exemple d'utilisation :
    • archiver des donnĂ©es
    • partitionner les donnĂ©es d'une table
    • dĂ©bugger une requĂŞte complexe

CTE en Ă©criture : exemple

WITH donnees_a_archiver AS (
DELETE FROM donnes_courantes
 WHERE date < '2015-01-01'
 RETURNING *
)
INSERT INTO donnes_archivees
SELECT * FROM donnees_a_archiver;

CTE récursive

  • SQL permet d'exprimer des rĂ©cursions
    • WITH RECURSIVE
  • UtilitĂ© :
    • rĂ©cupĂ©rer une arborescence de menu hiĂ©rarchique
    • parcourir des graphes (rĂ©seaux sociaux, etc.)

CTE récursive : exemple (1/2)

WITH RECURSIVE suite AS (
SELECT 1 AS valeur
UNION ALL
SELECT valeur + 1
  FROM suite
 WHERE valeur < 10
)
SELECT * FROM suite;

CTE récursive : principe

  • 1ère Ă©tape : initialisation de la rĂ©cursion

Principe CTE recursive - 1

CTE récursive : principe

  • rĂ©cursion : la requĂŞte s'appelle elle-mĂŞme

Principe CTE recursive - 2

CTE récursive : exemple (2/2)

WITH RECURSIVE parcours_menu AS (
SELECT menu_id, libelle, parent_id,
       libelle AS arborescence
  FROM entrees_menu
 WHERE libelle = 'Terminal'
   AND parent_id IS NULL
UNION ALL
SELECT menu.menu_id, menu.libelle, menu.parent_id,
       arborescence || '/' || menu.libelle
  FROM entrees_menu menu
  JOIN parcours_menu parent
    ON (menu.parent_id = parent.menu_id)
)
SELECT * FROM parcours_menu;

Concurrence d'accès

  • Problèmes pouvant se poser :
    • UPDATE perdu
    • lecture non rĂ©pĂ©table
  • Plusieurs solutions possibles
    • versionnement des lignes
    • SELECT FOR UPDATE
    • SERIALIZABLE

SELECT FOR UPDATE

  • SELECT FOR UPDATE
  • UtilitĂ© :
    • "rĂ©server" des lignes en vue de leur mise Ă  jour
    • Ă©viter les problèmes de concurrence d'accès

SKIP LOCKED

  • SELECT FOR UPDATE SKIP LOCKED
    • PostgreSQL 9.5
  • UtilitĂ© :
    • implĂ©mente des files d'attentes parallĂ©lisables

Serializable Snapshot Isolation

SSI : Serializable Snapshot Isolation (9.1+)

  • Chaque transaction est seule sur la base
  • Si on ne peut maintenir l'illusion
    • Une des transactions en cours est annulĂ©e
  • Sans blocage
  • On doit ĂŞtre capable de rejouer la transaction
  • Toutes les transactions impliquĂ©es doivent ĂŞtre serializable
  • default_transaction_isolation=serializable dans la configuration

Conclusion

  • SQL est un langage très riche
  • ConnaĂ®tre les nouveautĂ©s des versions de la norme depuis 20 ans permet de
    • gagner Ă©normĂ©ment de temps de dĂ©veloppemment
    • mais aussi de performance

Travaux Pratiques

SQL pour l'analyse de données

Préambule

  • Analyser des donnĂ©es est facile avec PostgreSQL
    • opĂ©rations d'agrĂ©gation disponibles
    • fonctions OLAP avancĂ©es
  • agrĂ©gation de donnĂ©es
  • clause FILTER
  • fonctions window
  • GROUPING SETS, ROLLUP, CUBE
  • WITHIN GROUPS

Objectifs

  • Écrire des requĂŞtes encore plus complexes
  • Analyser les donnĂ©es en amont
    • pour ne rĂ©cupĂ©rer que le rĂ©sultat

Agrégats

  • SQL dispose de fonctions de calcul d'agrĂ©gats
  • UtilitĂ© :
    • calcul de sommes, moyennes, valeur minimale et maximale
    • nombreuses fonctions statistiques disponibles

Agrégats avec GROUP BY

  • agrĂ©gat + GROUP BY
  • UtilitĂ©
    • effectue des calculs sur des regroupements : moyenne, somme, comptage, etc.
    • regroupement selon un critère dĂ©fini par la clause GROUP BY
    • exemple : calcul du salaire moyen de chaque service

GROUP BY : principe

RĂ©sultat du GROUP BY

GROUP BY : exemples

SELECT service,
       sum(salaire) AS salaires_par_service
  FROM employes
 GROUP BY service;

   service   | salaires_par_service
-------------+----------------------
 Courrier    |              7500.00
 Direction   |             10000.00
 Publication |              7000.00
(3 lignes)

Agrégats et ORDER BY

  • Extension propriĂ©taire de PostgreSQL
    • ORDER BY dans la fonction d'agrĂ©gat
  • UtilitĂ© :
    • ordonner les donnĂ©es agrĂ©gĂ©es
    • surtout utile avec array_agg, string_agg et xmlagg

Utiliser ORDER BY avec un agrégat

SELECT service,
       string_agg(nom, ', ' ORDER BY nom) AS liste_employes
  FROM employes
 GROUP BY service;
   service   |  liste_employes
-------------+-------------------
 Courrier    | Fantasio, Lagaffe
 Direction   | Dupuis
 Publication | Lebrac, Prunelle
(3 lignes)

Clause FILTER

  • Clause FILTER
  • UtilitĂ© :
    • filtrer les donnĂ©es sur les agrĂ©gats
    • Ă©vite les expressions CASE complexes
  • SQL:2003
  • IntĂ©grĂ© dans la version 9.4

Filtrer avec CASE

  • La syntaxe suivante Ă©tait utilisĂ©e :
SELECT count(*) AS compte_pays,
       count(CASE WHEN r.nom_region='Europe' THEN 1
                  ELSE 0
              END) AS compte_pays_europeens
  FROM pays p
  JOIN regions r
    ON (p.region_id = r.region_id);

Filtrer avec FILTER

  • La mĂŞme requĂŞte Ă©crite avec la clause FILTER :
SELECT count(*) AS compte_pays,
       count(*) FILTER (WHERE r.nom_region='Europe')
                AS compte_pays_europeens
  FROM pays p
  JOIN regions r
    ON (p.region_id = r.region_id);

Fonctions de fenĂŞtrage

  • Fonctions window
    • travaille sur des ensembles de donnĂ©es regroupĂ©s et triĂ©s indĂ©pendamment de la requĂŞte principale
  • Utilisation :
    • utiliser plusieurs critères d'agrĂ©gation dans la mĂŞme requĂŞte
    • utiliser des fonctions de classement
    • faire rĂ©fĂ©rence Ă  d'autres lignes de l'ensemble de donnĂ©es

Regroupement

  • Regroupement
    • clause OVER (PARTITION BY ...)
  • UtilitĂ© :
    • plusieurs critères de regroupement diffĂ©rents
    • avec des fonctions de calcul d'agrĂ©gats

Regroupement : exemple

SELECT matricule, salaire, service,
       SUM(salaire) OVER (PARTITION BY service)
                 AS total_salaire_service
  FROM employes;

 matricule | salaire  |   service   | total_salaire_service
-----------+----------+-------------+-----------------------
 00000004  |  4500.00 | Courrier    |               7500.00
 00000020  |  3000.00 | Courrier    |               7500.00
 00000001  | 10000.00 | Direction   |              10000.00
 00000006  |  4000.00 | Publication |               7000.00
 00000040  |  3000.00 | Publication |               7000.00

Regroupement : principe

SUM(salaire) OVER (PARTITION BY service)

Fonction de fenĂŞtrage

Regroupement : syntaxe

SELECT ...
 agregation OVER (PARTITION BY <colonnes>)
  FROM <liste_tables>
 WHERE <predicats>

Tri

  • Tri
    • OVER (ORDER BY …)
  • UtilitĂ© :
    • numĂ©roter les lignes : row_number()
    • classer des rĂ©sultats : rank(), dense_rank()
    • faire appel Ă  d'autres ligne du rĂ©sultat : lead(), lag()

Tri : exemple

  • Pour numĂ©roter des lignes :
SELECT row_number() OVER (ORDER BY matricule),
       matricule, nom
  FROM employes;

 row_number | matricule |   nom
------------+-----------+----------
          1 | 00000001  | Dupuis
          2 | 00000004  | Fantasio
          3 | 00000006  | Prunelle
          4 | 00000020  | Lagaffe
          5 | 00000040  | Lebrac
(5 lignes)

Tri : exemple avec une somme

  • Calcul d'une somme glissante :
SELECT matricule, salaire,
       SUM(salaire) OVER (ORDER BY matricule)
  FROM employes;

 matricule | salaire  |   sum
-----------+----------+----------
 00000001  | 10000.00 | 10000.00
 00000004  |  4500.00 | 14500.00
 00000006  |  4000.00 | 18500.00
 00000020  |  3000.00 | 21500.00
 00000040  |  3000.00 | 24500.00

Tri : principe

SUM(salaire) OVER (ORDER BY matricule)

Fonction de fenĂŞtrage - tru

Tri : syntaxe

SELECT ...
 agregation OVER (ORDER BY >colonnes>)
  FROM <liste_tables>
 WHERE <predicats>

Regroupement et tri

  • On peut combiner les deux
    • OVER (PARTITION BY .. ORDER BY ..)
  • UtilitĂ© :
    • travailler sur des jeux de donnĂ©es ordonnĂ©s et isolĂ©s les uns des autres

Regroupement et tri : exemple

SELECT continent, pays, population,
       rank() OVER (PARTITION BY continent
                    ORDER BY population DESC)
              AS rang
  FROM population;

    continent     |       pays         | population | rang
------------------+--------------------+------------+------
 Afrique          | Nigéria            |      173.6 |    1
 Afrique          | Éthiopie           |       94.1 |    2
 Afrique          | Égypte             |       82.1 |    3
 Afrique          | Rép. dém. du Congo |       67.5 |    4
(...)
 Amérique du Nord | États-Unis         |      320.1 |    1
 Amérique du Nord | Canada             |       35.2 |    2
(...)

Regroupement et tri : principe

OVER (PARTITION BY continent
      ORDER BY population DESC)

Fonction de fenĂŞtrage - partition et tri

Regroupement et tri : syntaxe

SELECT ...
 <agregation> OVER (PARTITION BY <colonnes>
                  ORDER BY <colonnes>)
  FROM <liste_tables>
 WHERE <predicats>

Fonctions analytiques

  • PostgreSQL dispose d'un certain nombre de fonctions analytiques
  • UtilitĂ© :
    • faire rĂ©fĂ©rence Ă  d'autres lignes du mĂŞme ensemble
    • Ă©vite les auto-jointures complexes et lentes

lead() et lag()

  • lead(colonne, n)
    • retourne la valeur d'une colonne, n lignes après la ligne courante
  • lag(colonne, n)
    • retourne la valeur d'une colonne, n lignes avant la ligne courante

lead() et lag() : exemple

SELECT pays, continent, population,
       lag(population) OVER (PARTITION BY continent
                             ORDER BY population DESC)
  FROM population;
         pays          | continent | population |  lag
-----------------------+-----------+------------+--------
 Chine                 | Asie      |     1385.6 |
 Iraq                  | Asie      |       33.8 | 1385.6
 Ouzbékistan           | Asie      |       28.9 |   33.8
 Arabie Saoudite       | Asie      |       28.8 |   28.9
 France métropolitaine | Europe    |       64.3 |
 Finlande              | Europe    |        5.4 |   64.3
 Lettonie              | Europe    |        2.1 |    5.4

lead() et lag() : principe

lag(population) OVER (PARTITION BY continent
                      ORDER BY population DESC)

Fonction lag()

first/last/nth_value

  • first_value(colonne)
    • retourne la dernière valeur pour la colonne
  • last_value(colonne)
    • retourne la dernière valeur pour la colonne
  • nth_value(colonne, n)
    • retourne la n-ème valeur (en comptant Ă  partir de 1) pour la colonne

first/last/nth_value : exemple

SELECT pays, continent, population,
       first_value(population)
           OVER (PARTITION BY continent
                 ORDER BY population DESC)
  FROM population;

       pays      | continent | population | first_value
-----------------+-----------+------------+-------------
 Chine           | Asie      |     1385.6 |      1385.6
 Iraq            | Asie      |       33.8 |      1385.6
 Ouzbékistan     | Asie      |       28.9 |      1385.6
 Arabie Saoudite | Asie      |       28.8 |      1385.6
 France          | Europe    |       64.3 |        64.3
 Finlande        | Europe    |        5.4 |        64.3
 Lettonie        | Europe    |        2.1 |        64.3

Clause WINDOW

  • Pour factoriser la dĂ©finition d'une fenĂŞtre :
SELECT matricule, nom, salaire, service,
       rank() OVER w,
       dense_rank() OVER w
  FROM employes
 WINDOW w AS (ORDER BY salaire);

Clause WINDOW : syntaxe

SELECT fonction_agregat OVER nom,
       fonction_agregat_2 OVER nom ...
       ...
  FROM <liste_tables>
 WHERE <predicats>
 WINDOW nom AS (PARTITION BY ... ORDER BY ...)

DĂ©finition de la fenĂŞtre

  • La fenĂŞtre de travail par dĂ©faut est :
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  • Deux modes possibles :
    • RANGE
    • ROWS
  • NĂ©cessite une clause ORDER BY

DĂ©finition de la fenĂŞtre : RANGE

  • Indique un intervalle Ă  bornes flou
  • Borne de dĂ©part :
    • UNBOUNDED PRECEDING: depuis le dĂ©but de la partition
    • CURRENT ROW : depuis la ligne courante
  • Borne de fin :
    • UNBOUNDED FOLLOWING : jusqu'Ă  la fin de la partition
    • CURRENT ROW : jusqu'Ă  la ligne courante

      OVER (PARTITION BY ...
        ORDER BY ...
        RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING

DĂ©finition de la fenĂŞtre : ROWS

  • Indique un intervalle bornĂ© par un nombre de ligne dĂ©fini avant et après la ligne courante
  • Borne de dĂ©part :
    • xxx PRECEDING : depuis les xxx valeurs devant la ligne courante
    • CURRENT ROW : depuis la ligne courante
  • Borne de fin :
    • xxx FOLLOWING : depuis les xxx valeurs derrière la ligne courante
    • CURRENT ROW : jusqu'Ă  la ligne courante

      OVER (PARTITION BY ...
        ORDER BY ...
        ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING

DĂ©finition de la fenĂŞtre : exemple

SELECT pays, continent, population,
       last_value(population)
        OVER (PARTITION BY continent ORDER BY population
              RANGE BETWEEN UNBOUNDED PRECEDING
                        AND UNBOUNDED FOLLOWING)
  FROM population;

         pays          | continent | population | last_value
-----------------------+-----------+------------+------------
 Arabie Saoudite       | Asie      |       28.8 |     1385.6
 Ouzbékistan           | Asie      |       28.9 |     1385.6
 Iraq                  | Asie      |       33.8 |     1385.6
 Chine (4)             | Asie      |     1385.6 |     1385.6
 Lettonie              | Europe    |        2.1 |       64.3
 Finlande              | Europe    |        5.4 |       64.3
 France métropolitaine | Europe    |       64.3 |       64.3

WITHIN GROUP

  • WITHIN GROUP
    • PostgreSQL 9.4
  • UtilitĂ© :
    • calcul de mĂ©dianes, centiles

WITHIN GROUP : exemple

SELECT continent,
  percentile_disc(0.5)
    WITHIN GROUP (ORDER BY population) AS "mediane",
  percentile_disc(0.95)
    WITHIN GROUP (ORDER BY population) AS "95pct",
  ROUND(AVG(population), 1) AS moyenne
FROM population
 GROUP BY continent;

         continent         | mediane | 95pct  | moyenne
---------------------------+---------+--------+---------
 Afrique                   |    33.0 |  173.6 |    44.3
 Amérique du Nord          |    35.2 |  320.1 |   177.7
 Amérique latine. Caraïbes |    30.4 |  200.4 |    53.3
 Asie                      |    53.3 | 1252.1 |   179.9
 Europe                    |     9.4 |   82.7 |    21.8

Grouping Sets

  • GROUPING SETS/ROLLUP/CUBE
  • Extension de GROUP BY
  • PostgreSQL 9.5
  • UtilitĂ© :
    • prĂ©sente le rĂ©sultat de plusieurs agrĂ©gations diffĂ©rentes
    • rĂ©aliser plusieurs agrĂ©gations diffĂ©rentes dans la mĂŞme requĂŞte

GROUPING SETS : jeu de données

Opérateur GROUP BY
Opérateur GROUP BY

GROUPING SETS : exemple visuel

Opérateur GROUP BY  

GROUPING SETS : exemple ordre sql

SELECT piece,region,sum(quantite)
FROM stock GROUP BY GROUPING SETS (piece,region);
 piece  | region | sum 
--------+--------+-----
 clous  |        |  70
 ecrous |        |  90
 vis    |        | 160
        | est    | 120
        | nord   |  60
        | ouest  |  50
        | sud    |  90

GROUPING SETS : Ă©quivalent

  • On peut se passer de la clause GROUPING SETS
    • mais la requĂŞte sera plus lente
SELECT piece,NULL as region,sum(quantite)
  FROM stock
  GROUP BY piece
UNION ALL
SELECT NULL, region,sum(quantite)
  FROM STOCK
  GROUP BY region;

ROLLUP

  • ROLLUP
  • PostgreSQL 9.5
  • UtilitĂ© :
    • calcul de totaux dans la mĂŞme requĂŞte

ROLLUP : exemple visuel

Opérateur GROUP BY  

ROLLUP : exemple ordre sql

SELECT piece,region,sum(quantite)
FROM stock GROUP BY ROLLUP (piece,region);

Cette requĂŞte est Ă©quivalente Ă  la requĂŞte suivante utilisant GROUPING SETS :

SELECT piece,region,sum(quantite)
FROM stock 
GROUP BY GROUPING SETS ((),(piece),(piece,region));

CUBE

  • CUBE
    • PostgreSQL 9.5
  • UtilitĂ© :
    • calcul de totaux dans la mĂŞme requĂŞte
    • sur toutes les clauses de regroupement

CUBE : exemple visuel

Opérateur GROUP BY  

CUBE : exemple ordre sql

SELECT piece,region,sum(quantite)
FROM stock GROUP BY CUBE (piece,region);

Cette requĂŞte est Ă©quivalente Ă  la requĂŞte suivante utilisant GROUPING SETS :

SELECT piece,region,sum(quantite)
FROM stock                              
GROUP BY GROUPING SETS (
  (),
  (piece),
  (region),
  (piece,region) 
  ); 

Travaux Pratiques


  1. Situation où deux sessions ou plus modifient des données en tables au même moment.↩

  2. La solution actuelle semble techniquement meilleure et la solution actuelle a donc été choisie. Le wiki du projet PostgreSQL montre que l'ordre MERGE a été étudié et qu'un certains nombres d' aspects cruciaux n'ont pas été spécifiés, amenant le projet PostgreSQL a utiliser sa propre version. Voir la documentation : https://wiki.postgresql.org/wiki/UPSERT#MERGE_disadvantages.↩