0% ont trouvé ce document utile (0 vote)
49 vues2 882 pages

PG 11

Transféré par

simbabre7
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
49 vues2 882 pages

PG 11

Transféré par

simbabre7
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Documentation PostgreSQL 11.

22

The PostgreSQL Global Development Group


Documentation PostgreSQL 11.22
The PostgreSQL Global Development Group
Copyright © 1996-2023 The PostgreSQL Global Development Group

Legal Notice
PostgreSQL is Copyright (c) 1996-2023 by the PostgreSQL Global Development Group.

Postgres95 is Copyright (c) 1994-5 by the Regents of the University of California.

Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written
agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.

IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE
AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED
HEREUNDER IS ON AN « AS-IS » BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Table des matières
Préface .................................................................................................................... xxxi
1. Définition de PostgreSQL ............................................................................... xxxi
2. Bref historique de PostgreSQL ........................................................................ xxxii
2.1. Le projet POSTGRES à Berkeley ......................................................... xxxii
2.2. Postgres95 ........................................................................................ xxxii
2.3. PostgreSQL ...................................................................................... xxxiii
3. Conventions ................................................................................................ xxxiii
4. Pour plus d'informations ............................................................................... xxxiii
5. Lignes de conduite pour les rapports de bogues ................................................. xxxiv
5.1. Identifier les bogues .......................................................................... xxxiv
5.2. Que rapporter ? .................................................................................. xxxv
5.3. Où rapporter des bogues ? .................................................................. xxxvii
I. Tutoriel .................................................................................................................... 1
1. Démarrage ........................................................................................................ 3
1.1. Installation ............................................................................................. 3
1.2. Concepts architecturaux de base ................................................................ 3
1.3. Création d'une base de données ................................................................. 4
1.4. Accéder à une base ................................................................................. 5
2. Le langage SQL ................................................................................................ 7
2.1. Introduction ............................................................................................ 7
2.2. Concepts ................................................................................................ 7
2.3. Créer une nouvelle table ........................................................................... 7
2.4. Remplir une table avec des lignes .............................................................. 8
2.5. Interroger une table ................................................................................. 9
2.6. Jointures entre les tables ......................................................................... 11
2.7. Fonctions d'agrégat ................................................................................ 13
2.8. Mises à jour ......................................................................................... 15
2.9. Suppressions ......................................................................................... 15
3. Fonctionnalités avancées ................................................................................... 16
3.1. Introduction .......................................................................................... 16
3.2. Vues .................................................................................................... 16
3.3. Clés étrangères ...................................................................................... 16
3.4. Transactions ......................................................................................... 17
3.5. Fonctions de fenêtrage ............................................................................ 19
3.6. Héritage ............................................................................................... 22
3.7. Conclusion ........................................................................................... 23
II. Langage SQL ......................................................................................................... 25
4. Syntaxe SQL ................................................................................................... 33
4.1. Structure lexicale ................................................................................... 33
4.2. Expressions de valeurs ........................................................................... 42
4.3. Fonctions appelantes .............................................................................. 57
5. Définition des données ...................................................................................... 60
5.1. Notions fondamentales sur les tables ......................................................... 60
5.2. Valeurs par défaut ................................................................................. 61
5.3. Contraintes ........................................................................................... 62
5.4. Colonnes système .................................................................................. 70
5.5. Modification des tables ........................................................................... 71
5.6. Droits .................................................................................................. 74
5.7. Politiques de sécurité niveau ligne ............................................................ 75
5.8. Schémas ............................................................................................... 82
5.9. L'héritage ............................................................................................. 86
5.10. Partitionnement de tables ...................................................................... 90
5.11. Données distantes ............................................................................... 104
5.12. Autres objets de la base de données ....................................................... 105
5.13. Gestion des dépendances ..................................................................... 105

iii
Documentation PostgreSQL 11.22

6. Manipulation de données ................................................................................. 108


6.1. Insérer des données .............................................................................. 108
6.2. Actualiser les données .......................................................................... 109
6.3. Supprimer des données ......................................................................... 110
6.4. Renvoyer des données provenant de lignes modifiées ................................. 110
7. Requêtes ....................................................................................................... 112
7.1. Aperçu ............................................................................................... 112
7.2. Expressions de table ............................................................................. 112
7.3. Listes de sélection ................................................................................ 128
7.4. Combiner des requêtes .......................................................................... 130
7.5. Tri des lignes ...................................................................................... 131
7.6. LIMIT et OFFSET .............................................................................. 132
7.7. Listes VALUES .................................................................................... 132
7.8. Requêtes WITH (Common Table Expressions) ........................................... 133
8. Types de données ........................................................................................... 140
8.1. Types numériques ................................................................................ 141
8.2. Types monétaires ................................................................................. 146
8.3. Types caractère ................................................................................... 147
8.4. Types de données binaires ..................................................................... 149
8.5. Types date/heure .................................................................................. 152
8.6. Type booléen ...................................................................................... 162
8.7. Types énumération ............................................................................... 163
8.8. Types géométriques .............................................................................. 165
8.9. Types adresses réseau ........................................................................... 167
8.10. Type chaîne de bits ............................................................................ 170
8.11. Types de recherche plein texte .............................................................. 171
8.12. Type UUID ....................................................................................... 174
8.13. Type XML ........................................................................................ 175
8.14. Types JSON ...................................................................................... 177
8.15. Tableaux ........................................................................................... 185
8.16. Types composites ............................................................................... 194
8.17. Types intervalle de valeurs .................................................................. 201
8.18. Types domaine .................................................................................. 207
8.19. Types identifiant d'objet ...................................................................... 207
8.20. pg_lsn Type ...................................................................................... 209
8.21. Pseudo-Types .................................................................................... 209
9. Fonctions et opérateurs .................................................................................... 211
9.1. Opérateurs logiques .............................................................................. 211
9.2. Fonctions et opérateurs de comparaison ................................................... 211
9.3. Fonctions et opérateurs mathématiques .................................................... 214
9.4. Fonctions et opérateurs de chaînes .......................................................... 218
9.5. Fonctions et opérateurs de chaînes binaires ............................................... 232
9.6. Fonctions et opérateurs sur les chaînes de bits ........................................... 234
9.7. Correspondance de motif ....................................................................... 235
9.8. Fonctions de formatage des types de données ............................................ 251
9.9. Fonctions et opérateurs sur date/heure ..................................................... 258
9.10. Fonctions de support enum .................................................................. 272
9.11. Fonctions et opérateurs géométriques ..................................................... 273
9.12. Fonctions et opérateurs sur les adresses réseau ......................................... 277
9.13. Fonctions et opérateurs de la recherche plein texte ................................... 279
9.14. Fonctions XML ................................................................................. 286
9.15. Fonctions et opérateurs JSON ............................................................... 300
9.16. Fonctions de manipulation de séquences ................................................. 310
9.17. Expressions conditionnelles .................................................................. 313
9.18. Fonctions et opérateurs de tableaux ....................................................... 315
9.19. Fonctions et opérateurs sur les données de type range ............................... 319
9.20. Fonctions d'agrégat ............................................................................. 321
9.21. Fonctions Window ............................................................................. 329

iv
Documentation PostgreSQL 11.22

9.22. Expressions de sous-requêtes ................................................................ 331


9.23. Comparaisons de lignes et de tableaux ................................................... 334
9.24. Fonctions retournant des ensembles ....................................................... 337
9.25. Fonctions d'informations système .......................................................... 340
9.26. Fonctions d'administration système ........................................................ 358
9.27. Fonctions trigger ................................................................................ 377
9.28. Fonctions des triggers sur les événements ............................................... 377
10. Conversion de types ...................................................................................... 381
10.1. Aperçu ............................................................................................. 381
10.2. Opérateurs ........................................................................................ 382
10.3. Fonctions .......................................................................................... 386
10.4. Stockage de valeurs ............................................................................ 390
10.5. Constructions UNION, CASE et constructions relatives .............................. 391
10.6. Colonnes de sortie du SELECT ............................................................ 393
11. Index .......................................................................................................... 394
11.1. Introduction ....................................................................................... 394
11.2. Types d'index .................................................................................... 395
11.3. Index multicolonnes ............................................................................ 397
11.4. Index et ORDER BY .......................................................................... 398
11.5. Combiner des index multiples .............................................................. 399
11.6. Index d'unicité ................................................................................... 399
11.7. Index d'expressions ............................................................................. 400
11.8. Index partiels ..................................................................................... 401
11.9. Parcours d'index seul et index couvrants ................................................. 404
11.10. Classes et familles d'opérateurs ........................................................... 407
11.11. Index et collationnements ................................................................... 408
11.12. Examiner l'utilisation des index ........................................................... 409
12. Recherche plein texte .................................................................................... 411
12.1. Introduction ....................................................................................... 411
12.2. Tables et index .................................................................................. 415
12.3. Contrôler la recherche plein texte .......................................................... 418
12.4. Fonctionnalités supplémentaires ............................................................ 425
12.5. Analyseurs ........................................................................................ 431
12.6. Dictionnaires ..................................................................................... 433
12.7. Exemple de configuration .................................................................... 443
12.8. Tester et déboguer la recherche plein texte .............................................. 445
12.9. Types d'index préférées pour la recherche plein texte ................................ 450
12.10. Support de psql ................................................................................ 451
12.11. Limites ........................................................................................... 454
13. Contrôle d'accès simultané ............................................................................. 455
13.1. Introduction ....................................................................................... 455
13.2. Isolation des transactions ..................................................................... 455
13.3. Verrouillage explicite .......................................................................... 462
13.4. Vérification de cohérence des données au niveau de l'application ................. 467
13.5. Avertissements ................................................................................... 469
13.6. Verrous et index ................................................................................ 469
14. Conseils sur les performances ......................................................................... 471
14.1. Utiliser EXPLAIN .............................................................................. 471
14.2. Statistiques utilisées par le planificateur ................................................. 483
14.3. Contrôler le planificateur avec des clauses JOIN explicites ........................ 487
14.4. Remplir une base de données ............................................................... 489
14.5. Configuration avec une perte acceptée ................................................... 492
15. Requêtes parallélisées .................................................................................... 494
15.1. Comment fonctionne la parallélisation des requêtes .................................. 494
15.2. Quand la parallélisation des requêtes peut-elle être utilisée ? ....................... 495
15.3. Plans parallélisés ................................................................................ 496
15.4. Sécurité de la parallélisation ................................................................. 498
III. Administration du serveur ...................................................................................... 500

v
Documentation PostgreSQL 11.22

16. Procédure d'installation du code source ............................................................. 506


16.1. Version courte ................................................................................... 506
16.2. Prérequis .......................................................................................... 506
16.3. Obtenir les sources ............................................................................. 508
16.4. Procédure d'installation ....................................................................... 508
16.5. Initialisation post-installation ................................................................ 523
16.6. Plateformes supportées ........................................................................ 524
16.7. Notes spécifiques à des plateformes ....................................................... 525
17. Installation à partir du code source sur Windows ................................................ 533
17.1. Construire avec Visual C++ ou le Microsoft Windows SDK ....................... 533
18. Configuration du serveur et mise en place ......................................................... 540
18.1. Compte utilisateur PostgreSQL ............................................................. 540
18.2. Créer un groupe de base de données ...................................................... 540
18.3. Lancer le serveur de bases de données ................................................... 542
18.4. Gérer les ressources du noyau .............................................................. 545
18.5. Arrêter le serveur ............................................................................... 556
18.6. Mise à jour d'une instance PostgreSQL .................................................. 556
18.7. Empêcher l'usurpation de serveur .......................................................... 559
18.8. Options de chiffrement ........................................................................ 560
18.9. Connexions tcp/ip sécurisées avec ssl .................................................... 561
18.10. Connexions tcp/ip sécurisées avec des tunnels ssh tunnels ........................ 565
18.11. Enregistrer le journal des événements sous Windows .............................. 566
19. Configuration du serveur ................................................................................ 567
19.1. Paramètres de configuration ................................................................. 567
19.2. Emplacement des fichiers .................................................................... 571
19.3. Connexions et authentification .............................................................. 572
19.4. Consommation des ressources .............................................................. 578
19.5. Write Ahead Log ............................................................................... 586
19.6. Réplication ........................................................................................ 593
19.7. Planification des requêtes .................................................................... 598
19.8. Remonter et tracer les erreurs ............................................................... 605
19.9. Statistiques d'exécution ....................................................................... 617
19.10. Nettoyage (vacuum) automatique ....................................................... 618
19.11. Valeurs par défaut des connexions client ............................................... 620
19.12. Gestion des verrous .......................................................................... 630
19.13. Compatibilité de version et de plateforme ............................................. 631
19.14. Gestion des erreurs ........................................................................... 633
19.15. Options préconfigurées ...................................................................... 634
19.16. Options personnalisées ...................................................................... 636
19.17. Options pour les développeurs ............................................................ 636
19.18. Options courtes ................................................................................ 640
20. Authentification du client ............................................................................... 641
20.1. Le fichier pg_hba.conf ................................................................... 641
20.2. Correspondances d'utilisateurs .............................................................. 649
20.3. Méthodes d'authentification .................................................................. 651
20.4. Authentification trust .......................................................................... 651
20.5. Authentification par mot de passe ......................................................... 652
20.6. Authentification GSSAPI ..................................................................... 653
20.7. Authentification SSPI .......................................................................... 655
20.8. Authentification fondée sur ident .......................................................... 656
20.9. Authentification Peer .......................................................................... 656
20.10. Authentification LDAP ...................................................................... 657
20.11. Authentification RADIUS .................................................................. 659
20.12. Authentification de certificat ............................................................... 661
20.13. Authentification PAM ....................................................................... 661
20.14. Authentification BSD ........................................................................ 662
20.15. Problèmes d'authentification ............................................................... 662
21. Rôles de la base de données ........................................................................... 664

vi
Documentation PostgreSQL 11.22

21.1. Rôles de la base de données ................................................................. 664


21.2. Attributs des rôles .............................................................................. 665
21.3. Appartenance d'un rôle ........................................................................ 667
21.4. Supprimer des rôles ............................................................................ 668
21.5. Rôles par défaut ................................................................................. 669
21.6. Sécurité des fonctions ......................................................................... 670
22. Administration des bases de données ................................................................ 672
22.1. Aperçu ............................................................................................. 672
22.2. Création d'une base de données ............................................................ 673
22.3. Bases de données modèles ................................................................... 674
22.4. Configuration d'une base de données ..................................................... 675
22.5. Détruire une base de données ............................................................... 675
22.6. Tablespaces ....................................................................................... 675
23. Localisation ................................................................................................. 678
23.1. Support des locales ............................................................................. 678
23.2. Support des collations ......................................................................... 680
23.3. Support des jeux de caractères .............................................................. 687
24. Planifier les tâches de maintenance .................................................................. 693
24.1. Nettoyages réguliers ........................................................................... 693
24.2. Ré-indexation régulière ....................................................................... 702
24.3. Maintenance du fichier de traces ........................................................... 702
25. Sauvegardes et restaurations ........................................................................... 704
25.1. Sauvegarde SQL ................................................................................ 704
25.2. Sauvegarde de niveau système de fichiers ............................................... 707
25.3. Archivage continu et récupération d'un instantané (PITR) .......................... 708
26. Haute disponibilité, répartition de charge et réplication ........................................ 721
26.1. Comparaison de différentes solutions ..................................................... 721
26.2. Serveurs de Standby par transfert de journaux ......................................... 725
26.3. Bascule (Failover) .............................................................................. 735
26.4. Méthode alternative pour le log shipping ................................................ 736
26.5. Hot Standby ...................................................................................... 738
27. Configuration de la récupération ...................................................................... 746
27.1. Paramètres de récupération de l'archive .................................................. 746
27.2. Paramètres de cible de récupération ....................................................... 747
27.3. Paramètres de serveur de Standby ......................................................... 748
28. Surveiller l'activité de la base de données ......................................................... 751
28.1. Outils Unix standard ........................................................................... 751
28.2. Le récupérateur de statistiques .............................................................. 752
28.3. Visualiser les verrous .......................................................................... 788
28.4. Rapporter la progression ...................................................................... 789
28.5. Traces dynamiques ............................................................................. 791
29. Surveiller l'utilisation des disques .................................................................... 802
29.1. Déterminer l'utilisation des disques ........................................................ 802
29.2. Panne pour disque saturé ..................................................................... 803
30. Fiabilité et journaux de transaction .................................................................. 804
30.1. Fiabilité ............................................................................................ 804
30.2. Write-Ahead Logging (WAL) ............................................................... 806
30.3. Validation asynchrone (Asynchronous Commit) ....................................... 807
30.4. Configuration des journaux de transaction .............................................. 808
30.5. Vue interne des journaux de transaction ................................................. 811
31. Réplication logique ....................................................................................... 813
31.1. Publication ........................................................................................ 813
31.2. Abonnement ...................................................................................... 814
31.3. Conflits ............................................................................................ 815
31.4. Restrictions ....................................................................................... 816
31.5. Architecture ...................................................................................... 816
31.6. Supervision ....................................................................................... 817
31.7. Sécurité ............................................................................................ 817

vii
Documentation PostgreSQL 11.22

31.8. Paramètres de configuration ................................................................. 818


31.9. Démarrage rapide ............................................................................... 818
32. JIT (compilation à la volée) ............................................................................ 820
32.1. Qu'est-ce que le JIT ? ......................................................................... 820
32.2. Quand utiliser le JIT ? ......................................................................... 820
32.3. Configuration .................................................................................... 822
32.4. Extensibilité ...................................................................................... 822
33. Tests de régression ....................................................................................... 824
33.1. Lancer les tests .................................................................................. 824
33.2. Évaluation des tests ............................................................................ 828
33.3. Fichiers de comparaison de variants ...................................................... 830
33.4. TAP Tests ......................................................................................... 831
33.5. Examen de la couverture du test ........................................................... 832
IV. Interfaces client .................................................................................................... 833
34. libpq - Bibliothèque C ................................................................................... 838
34.1. Fonctions de contrôle de connexion à la base de données ........................... 838
34.2. Fonctions de statut de connexion .......................................................... 852
34.3. Fonctions d'exécution de commandes ..................................................... 859
34.4. Traitement des commandes asynchrones ................................................. 876
34.5. Récupérer le résultats des requêtes ligne par ligne .................................... 880
34.6. Annuler des requêtes en cours d'exécution .............................................. 881
34.7. Interface rapide (Fast Path) .................................................................. 882
34.8. Notification asynchrone ....................................................................... 883
34.9. Fonctions associées à la commande COPY .............................................. 884
34.10. Fonctions de contrôle ........................................................................ 889
34.11. Fonctions diverses ............................................................................ 891
34.12. Traitement des messages .................................................................... 894
34.13. Système d'événements ....................................................................... 895
34.14. Variables d'environnement ................................................................. 902
34.15. Fichier de mots de passe .................................................................... 904
34.16. Fichier des services de connexion ........................................................ 905
34.17. Recherche LDAP des paramètres de connexion ...................................... 905
34.18. Support de SSL ................................................................................ 906
34.19. Comportement des programmes threadés .............................................. 911
34.20. Construire des applications avec libpq .................................................. 911
34.21. Exemples de programmes .................................................................. 913
35. Objets larges ................................................................................................ 925
35.1. Introduction ....................................................................................... 925
35.2. Fonctionnalités d'implémentation .......................................................... 925
35.3. Interfaces client ................................................................................. 925
35.4. Fonctions du côté serveur .................................................................... 930
35.5. Programme d'exemple ......................................................................... 931
36. ECPG SQL embarqué en C ............................................................................ 937
36.1. Le Concept ....................................................................................... 937
36.2. Gérer les Connexions à la Base de Données ............................................ 937
36.3. Exécuter des Commandes SQL ............................................................. 941
36.4. Utiliser des Variables Hôtes ................................................................. 944
36.5. SQL Dynamique ................................................................................ 959
36.6. Librairie pgtypes ................................................................................ 961
36.7. Utiliser les Zones de Descripteur .......................................................... 976
36.8. Gestion des Erreurs ............................................................................ 990
36.9. Directives de Préprocesseur ................................................................. 997
36.10. Traiter des Programmes en SQL Embarqué ......................................... 1000
36.11. Fonctions de la Librairie .................................................................. 1001
36.12. Large Objects ................................................................................. 1001
36.13. Applications C++ ............................................................................ 1003
36.14. Commandes SQL Embarquées .......................................................... 1007
36.15. Mode de Compatibilité Informix ........................................................ 1031

viii
Documentation PostgreSQL 11.22

36.16. Mode de compatibilité Oracle ........................................................... 1047


36.17. Fonctionnement Interne .................................................................... 1048
37. Schéma d'information .................................................................................. 1051
37.1. Le schéma ....................................................................................... 1051
37.2. Types de données ............................................................................. 1051
37.3. information_schema_catalog_name ........................................ 1052
37.4. administrable_role_authorizations .................................... 1052
37.5. applicable_roles ..................................................................... 1052
37.6. attributes ................................................................................. 1053
37.7. character_sets ......................................................................... 1056
37.8. check_constraint_routine_usage .......................................... 1057
37.9. check_constraints ................................................................... 1058
37.10. collations ................................................................................ 1058
37.11. collation_character_set_applicability .......................... 1059
37.12. column_domain_usage .............................................................. 1059
37.13. column_options ........................................................................ 1059
37.14. column_privileges .................................................................. 1060
37.15. column_udt_usage .................................................................... 1060
37.16. columns ...................................................................................... 1061
37.17. constraint_column_usage ...................................................... 1065
37.18. constraint_table_usage ........................................................ 1065
37.19. data_type_privileges ............................................................ 1066
37.20. domain_constraints ................................................................ 1066
37.21. domain_udt_usage .................................................................... 1067
37.22. domains ...................................................................................... 1067
37.23. element_types .......................................................................... 1069
37.24. enabled_roles .......................................................................... 1072
37.25. foreign_data_wrapper_options ............................................ 1072
37.26. foreign_data_wrappers .......................................................... 1072
37.27. foreign_server_options ........................................................ 1073
37.28. foreign_servers ...................................................................... 1073
37.29. foreign_table_options .......................................................... 1074
37.30. foreign_tables ........................................................................ 1074
37.31. key_column_usage .................................................................... 1075
37.32. parameters ................................................................................ 1075
37.33. referential_constraints ...................................................... 1077
37.34. role_column_grants ................................................................ 1078
37.35. role_routine_grants .............................................................. 1078
37.36. role_table_grants .................................................................. 1079
37.37. role_udt_grants ...................................................................... 1079
37.38. role_usage_grants .................................................................. 1080
37.39. routine_privileges ................................................................ 1080
37.40. routines .................................................................................... 1081
37.41. schemata .................................................................................... 1086
37.42. sequences .................................................................................. 1086
37.43. sql_features ............................................................................ 1087
37.44. sql_implementation_info ...................................................... 1088
37.45. sql_languages .......................................................................... 1088
37.46. sql_packages ............................................................................ 1089
37.47. sql_parts .................................................................................. 1089
37.48. sql_sizing ................................................................................ 1090
37.49. sql_sizing_profiles .............................................................. 1090
37.50. table_constraints .................................................................. 1090
37.51. table_privileges .................................................................... 1091
37.52. tables ........................................................................................ 1092
37.53. transforms ................................................................................ 1092
37.54. triggered_update_columns .................................................... 1093
37.55. triggers .................................................................................... 1094

ix
Documentation PostgreSQL 11.22

37.56. udt_privileges ........................................................................ 1095


37.57. usage_privileges .................................................................... 1096
37.58. user_defined_types ................................................................ 1096
37.59. user_mapping_options ............................................................ 1098
37.60. user_mappings .......................................................................... 1098
37.61. view_column_usage .................................................................. 1099
37.62. view_routine_usage ................................................................ 1099
37.63. view_table_usage .................................................................... 1100
37.64. views .......................................................................................... 1100
V. Programmation serveur ......................................................................................... 1102
38. Étendre SQL .............................................................................................. 1107
38.1. L'extensibilité ................................................................................... 1107
38.2. Le système des types de PostgreSQL ................................................... 1107
38.3. Fonctions utilisateur .......................................................................... 1109
38.4. Procédures utilisateur ........................................................................ 1110
38.5. Fonctions en langage de requêtes (SQL) ............................................... 1110
38.6. Surcharge des fonctions ..................................................................... 1127
38.7. Catégories de volatilité des fonctions ................................................... 1128
38.8. Fonctions en langage de procédures ..................................................... 1129
38.9. Fonctions internes ............................................................................. 1129
38.10. Fonctions en langage C .................................................................... 1130
38.11. Agrégats utilisateur ......................................................................... 1152
38.12. Types utilisateur ............................................................................. 1160
38.13. Opérateurs définis par l'utilisateur ...................................................... 1164
38.14. Informations sur l'optimisation d'un opérateur ...................................... 1165
38.15. Interfacer des extensions d'index ........................................................ 1169
38.16. Empaqueter des objets dans une extension ........................................... 1183
38.17. Outils de construction d'extension ...................................................... 1192
39. Déclencheurs (triggers) ................................................................................ 1197
39.1. Aperçu du comportement des déclencheurs ........................................... 1197
39.2. Visibilité des modifications des données ............................................... 1200
39.3. Écrire des fonctions déclencheurs en C ................................................. 1201
39.4. Un exemple complet de trigger ........................................................... 1203
40. Déclencheurs (triggers) sur événement ............................................................ 1207
40.1. Aperçu du fonctionnement des triggers sur événement ............................. 1207
40.2. Matrice de déclenchement des triggers sur événement ............................. 1208
40.3. Écrire des fonctions trigger sur événement en C ..................................... 1213
40.4. Un exemple complet de trigger sur événement ....................................... 1215
40.5. Un exemple de trigger sur événement de table modifiée ........................... 1216
41. Système de règles ....................................................................................... 1218
41.1. Arbre de requêtes ............................................................................. 1218
41.2. Vues et système de règles .................................................................. 1220
41.3. Vues matérialisées ............................................................................ 1227
41.4. Règles sur insert, update et delete ............................................ 1230
41.5. Règles et droits ................................................................................ 1242
41.6. Règles et statut de commande ............................................................. 1244
41.7. Règles contre déclencheurs ................................................................. 1245
42. Langages de procédures ............................................................................... 1248
42.1. Installation des langages de procédures ................................................. 1248
43. PL/pgSQL - Langage de procédures SQL ........................................................ 1251
43.1. Aperçu ............................................................................................ 1251
43.2. Structure de PL/pgSQL ..................................................................... 1252
43.3. Déclarations ..................................................................................... 1254
43.4. Expressions ..................................................................................... 1260
43.5. Instructions de base .......................................................................... 1260
43.6. Structures de contrôle ........................................................................ 1269
43.7. Curseurs .......................................................................................... 1284
43.8. Gestion des transactions ..................................................................... 1290

x
Documentation PostgreSQL 11.22

43.9. Erreurs et messages .......................................................................... 1292


43.10. Fonctions trigger ............................................................................. 1294
43.11. Les dessous de PL/pgSQL ................................................................ 1303
43.12. Astuces pour développer en PL/pgSQL ............................................... 1307
43.13. Portage d'Oracle PL/SQL ................................................................. 1310
44. PL/Tcl - Langage de procédures Tcl ............................................................... 1320
44.1. Aperçu ............................................................................................ 1320
44.2. Fonctions et arguments PL/Tcl ............................................................ 1320
44.3. Valeurs des données avec PL/Tcl ........................................................ 1322
44.4. Données globales avec PL/Tcl ............................................................ 1323
44.5. Accès à la base de données depuis PL/Tcl ............................................. 1323
44.6. Fonctions triggers en PL/Tcl ............................................................... 1326
44.7. Fonctions trigger sur événement en PL/Tcl ............................................ 1328
44.8. Gestion des erreurs avec PL/Tcl .......................................................... 1328
44.9. Sous-transactions explicites dans PL/Tcl ............................................... 1329
44.10. Gestion des transactions ................................................................... 1330
44.11. Configuration PL/Tcl ....................................................................... 1331
44.12. Noms de procédure Tcl .................................................................... 1331
45. PL/Perl - Langage de procédures Perl ............................................................. 1333
45.1. Fonctions et arguments PL/Perl ........................................................... 1333
45.2. Valeurs en PL/Perl ............................................................................ 1337
45.3. Fonction incluses .............................................................................. 1337
45.4. Valeurs globales dans PL/Perl ............................................................. 1343
45.5. Niveaux de confiance de PL/Perl ......................................................... 1344
45.6. Déclencheurs PL/Perl ........................................................................ 1345
45.7. Triggers sur événements avec PL/Perl .................................................. 1346
45.8. PL/Perl sous le capot ........................................................................ 1347
46. PL/Python - Langage de procédures Python ..................................................... 1349
46.1. Python 2 et Python 3 ........................................................................ 1349
46.2. Fonctions PL/Python ......................................................................... 1350
46.3. Valeur des données avec PL/Python ..................................................... 1352
46.4. Sharing Data .................................................................................... 1357
46.5. Blocs de code anonymes .................................................................... 1358
46.6. Fonctions de déclencheurs .................................................................. 1358
46.7. Accès à la base de données ................................................................ 1359
46.8. Sous-transactions explicites ................................................................ 1363
46.9. Gestion des transactions ..................................................................... 1365
46.10. Fonctions outils .............................................................................. 1365
46.11. Variables d'environnement ................................................................ 1366
47. Interface de programmation serveur ................................................................ 1368
47.1. Fonctions d'interface ......................................................................... 1368
47.2. Fonctions de support d'interface .......................................................... 1401
47.3. Gestion de la mémoire ...................................................................... 1410
47.4. Gestion des transactions ..................................................................... 1420
47.5. Visibilité des modifications de données ................................................ 1423
47.6. Exemples ........................................................................................ 1423
48. Processus en tâche de fond (background worker) .............................................. 1427
49. Décodage logique (Logical Decoding) ............................................................ 1431
49.1. Exemples de décodage logique ........................................................... 1431
49.2. Concepts de décodage logique ............................................................ 1434
49.3. Interface du protocole de réplication par flux ......................................... 1435
49.4. Interface SQL de décodage logique ...................................................... 1435
49.5. Catalogues systèmes liés au décodage logique ....................................... 1436
49.6. Plugins de sortie de décodage logique .................................................. 1436
49.7. Écrivains de sortie de décodage logique ................................................ 1440
49.8. Support de la réplication synchrone pour le décodage logique ................... 1440
50. Tracer la progression de la réplication ............................................................ 1442
VI. Référence .......................................................................................................... 1443

xi
Documentation PostgreSQL 11.22

I. Commandes SQL .......................................................................................... 1448


ABORT .................................................................................................. 1452
ALTER AGGREGATE ............................................................................. 1453
ALTER COLLATION .............................................................................. 1455
ALTER CONVERSION ............................................................................ 1457
ALTER DATABASE ................................................................................ 1459
ALTER DEFAULT PRIVILEGES .............................................................. 1462
ALTER DOMAIN .................................................................................... 1466
ALTER EVENT TRIGGER ....................................................................... 1470
ALTER EXTENSION ............................................................................... 1471
ALTER FOREIGN DATA WRAPPER ........................................................ 1475
ALTER FOREIGN TABLE ....................................................................... 1477
ALTER FUNCTION ................................................................................. 1482
ALTER GROUP ...................................................................................... 1486
ALTER INDEX ....................................................................................... 1488
ALTER LANGUAGE ............................................................................... 1491
ALTER LARGE OBJECT ......................................................................... 1492
ALTER MATERIALIZED VIEW ............................................................... 1493
ALTER OPERATOR ................................................................................ 1495
ALTER OPERATOR CLASS .................................................................... 1497
ALTER OPERATOR FAMILY .................................................................. 1498
ALTER POLICY ..................................................................................... 1502
ALTER PROCEDURE .............................................................................. 1504
ALTER PUBLICATION ........................................................................... 1507
ALTER ROLE ......................................................................................... 1509
ALTER ROUTINE ................................................................................... 1513
ALTER RULE ......................................................................................... 1515
ALTER SCHEMA ................................................................................... 1516
ALTER SEQUENCE ................................................................................ 1517
ALTER SERVER ..................................................................................... 1520
ALTER STATISTICS ............................................................................... 1522
ALTER SUBSCRIPTION .......................................................................... 1523
ALTER SYSTEM .................................................................................... 1526
ALTER TABLE ....................................................................................... 1528
ALTER TABLESPACE ............................................................................ 1545
ALTER TEXT SEARCH CONFIGURATION .............................................. 1547
ALTER TEXT SEARCH DICTIONARY ..................................................... 1549
ALTER TEXT SEARCH PARSER ............................................................. 1551
ALTER TEXT SEARCH TEMPLATE ........................................................ 1552
ALTER TRIGGER ................................................................................... 1553
ALTER TYPE ......................................................................................... 1555
ALTER USER ......................................................................................... 1559
ALTER USER MAPPING ......................................................................... 1560
ALTER VIEW ......................................................................................... 1562
ANALYZE .............................................................................................. 1564
BEGIN ................................................................................................... 1567
CALL ..................................................................................................... 1569
CHECKPOINT ........................................................................................ 1570
CLOSE ................................................................................................... 1571
CLUSTER .............................................................................................. 1573
COMMENT ............................................................................................ 1576
COMMIT ................................................................................................ 1581
COMMIT PREPARED ............................................................................. 1582
COPY .................................................................................................... 1583
CREATE ACCESS METHOD ................................................................... 1594
CREATE AGGREGATE ........................................................................... 1595
CREATE CAST ....................................................................................... 1603
CREATE COLLATION ............................................................................ 1608

xii
Documentation PostgreSQL 11.22

CREATE CONVERSION .......................................................................... 1610


CREATE DATABASE ............................................................................. 1612
CREATE DOMAIN ................................................................................. 1616
CREATE EVENT TRIGGER ..................................................................... 1619
CREATE EXTENSION ............................................................................ 1621
CREATE FOREIGN DATA WRAPPER ...................................................... 1624
CREATE FOREIGN TABLE ..................................................................... 1626
CREATE FUNCTION .............................................................................. 1631
CREATE GROUP .................................................................................... 1640
CREATE INDEX ..................................................................................... 1641
CREATE LANGUAGE ............................................................................. 1650
CREATE MATERIALIZED VIEW ............................................................. 1653
CREATE OPERATOR .............................................................................. 1655
CREATE OPERATOR CLASS .................................................................. 1658
CREATE OPERATOR FAMILY ................................................................ 1661
CREATE POLICY ................................................................................... 1662
CREATE PROCEDURE ........................................................................... 1668
CREATE PUBLICATION ......................................................................... 1672
CREATE ROLE ...................................................................................... 1674
CREATE RULE ...................................................................................... 1679
CREATE SCHEMA ................................................................................. 1682
CREATE SEQUENCE .............................................................................. 1685
CREATE SERVER .................................................................................. 1689
CREATE STATISTICS ............................................................................. 1691
CREATE SUBSCRIPTION ....................................................................... 1693
CREATE TABLE .................................................................................... 1696
CREATE TABLE AS ............................................................................... 1718
CREATE TABLESPACE .......................................................................... 1721
CREATE TEXT SEARCH CONFIGURATION ............................................ 1723
CREATE TEXT SEARCH DICTIONARY ................................................... 1725
CREATE TEXT SEARCH PARSER ........................................................... 1727
CREATE TEXT SEARCH TEMPLATE ...................................................... 1729
CREATE TRANSFORM ........................................................................... 1731
CREATE TRIGGER ................................................................................. 1734
CREATE TYPE ....................................................................................... 1742
CREATE USER ....................................................................................... 1751
CREATE USER MAPPING ....................................................................... 1752
CREATE VIEW ...................................................................................... 1754
DEALLOCATE ....................................................................................... 1759
DECLARE .............................................................................................. 1760
DELETE ................................................................................................. 1764
DISCARD ............................................................................................... 1767
DO ........................................................................................................ 1769
DROP ACCESS METHOD ....................................................................... 1771
DROP AGGREGATE ............................................................................... 1772
DROP CAST ........................................................................................... 1774
DROP COLLATION ................................................................................ 1775
DROP CONVERSION .............................................................................. 1776
DROP DATABASE ................................................................................. 1777
DROP DOMAIN ...................................................................................... 1778
DROP EVENT TRIGGER ......................................................................... 1779
DROP EXTENSION ................................................................................. 1780
DROP FOREIGN DATA WRAPPER .......................................................... 1782
DROP FOREIGN TABLE ......................................................................... 1783
DROP FUNCTION .................................................................................. 1785
DROP GROUP ........................................................................................ 1787
DROP INDEX ......................................................................................... 1788
DROP LANGUAGE ................................................................................. 1790

xiii
Documentation PostgreSQL 11.22

DROP MATERIALIZED VIEW ................................................................. 1792


DROP OPERATOR .................................................................................. 1793
DROP OPERATOR CLASS ...................................................................... 1795
DROP OPERATOR FAMILY .................................................................... 1797
DROP OWNED ....................................................................................... 1799
DROP POLICY ....................................................................................... 1800
DROP PROCEDURE ............................................................................... 1801
DROP PUBLICATION ............................................................................. 1803
DROP ROLE .......................................................................................... 1804
DROP ROUTINE ..................................................................................... 1805
DROP RULE .......................................................................................... 1806
DROP SCHEMA ..................................................................................... 1807
DROP SEQUENCE .................................................................................. 1809
DROP SERVER ...................................................................................... 1810
DROP STATISTICS ................................................................................. 1811
DROP SUBSCRIPTION ............................................................................ 1812
DROP TABLE ........................................................................................ 1814
DROP TABLESPACE .............................................................................. 1815
DROP TEXT SEARCH CONFIGURATION ................................................ 1816
DROP TEXT SEARCH DICTIONARY ....................................................... 1817
DROP TEXT SEARCH PARSER ............................................................... 1818
DROP TEXT SEARCH TEMPLATE .......................................................... 1819
DROP TRANSFORM ............................................................................... 1820
DROP TRIGGER ..................................................................................... 1822
DROP TYPE ........................................................................................... 1823
DROP USER ........................................................................................... 1824
DROP USER MAPPING ........................................................................... 1825
DROP VIEW .......................................................................................... 1826
END ...................................................................................................... 1827
EXECUTE .............................................................................................. 1828
EXPLAIN ............................................................................................... 1829
FETCH ................................................................................................... 1834
GRANT .................................................................................................. 1838
IMPORT FOREIGN SCHEMA .................................................................. 1846
INSERT .................................................................................................. 1848
LISTEN .................................................................................................. 1856
LOAD .................................................................................................... 1858
LOCK .................................................................................................... 1859
MOVE ................................................................................................... 1862
NOTIFY ................................................................................................. 1864
PREPARE ............................................................................................... 1867
PREPARE TRANSACTION ...................................................................... 1870
REASSIGN OWNED ............................................................................... 1872
REFRESH MATERIALIZED VIEW ........................................................... 1873
REINDEX ............................................................................................... 1875
RELEASE SAVEPOINT ........................................................................... 1878
RESET ................................................................................................... 1880
REVOKE ................................................................................................ 1881
ROLLBACK ........................................................................................... 1885
ROLLBACK PREPARED ......................................................................... 1886
ROLLBACK TO SAVEPOINT .................................................................. 1887
SAVEPOINT ........................................................................................... 1889
SECURITY LABEL ................................................................................. 1891
SELECT ................................................................................................. 1894
SELECT INTO ........................................................................................ 1916
SET ....................................................................................................... 1918
SET CONSTRAINTS ............................................................................... 1921
SET ROLE ............................................................................................. 1923

xiv
Documentation PostgreSQL 11.22

SET SESSION AUTHORIZATION ............................................................ 1925


SET TRANSACTION ............................................................................... 1927
SHOW ................................................................................................... 1930
START TRANSACTION .......................................................................... 1932
TRUNCATE ........................................................................................... 1933
UNLISTEN ............................................................................................. 1936
UPDATE ................................................................................................ 1938
VACUUM .............................................................................................. 1943
VALUES ................................................................................................ 1946
II. Applications client de PostgreSQL .................................................................. 1949
clusterdb ................................................................................................. 1950
createdb .................................................................................................. 1953
createuser ................................................................................................ 1956
dropdb .................................................................................................... 1960
dropuser .................................................................................................. 1963
ecpg ....................................................................................................... 1966
pg_basebackup ......................................................................................... 1969
pgbench .................................................................................................. 1977
pg_config ................................................................................................ 1994
pg_dump ................................................................................................. 1997
pg_dumpall ............................................................................................. 2010
pg_isready ............................................................................................... 2017
pg_receivewal .......................................................................................... 2019
pg_recvlogical ......................................................................................... 2024
pg_restore ............................................................................................... 2028
psql ........................................................................................................ 2037
reindexdb ................................................................................................ 2080
vacuumdb ............................................................................................... 2083
III. Applications relatives au serveur PostgreSQL .................................................. 2087
initdb ..................................................................................................... 2088
pg_archivecleanup .................................................................................... 2093
pg_controldata ......................................................................................... 2095
pg_ctl ..................................................................................................... 2096
pg_resetwal ............................................................................................. 2102
pg_rewind ............................................................................................... 2106
pg_test_fsync ........................................................................................... 2109
pg_test_timing ......................................................................................... 2110
pg_upgrade .............................................................................................. 2114
pg_verify_checksums ................................................................................ 2123
pg_waldump ............................................................................................ 2124
postgres .................................................................................................. 2126
postmaster ............................................................................................... 2134
VII. Internes ............................................................................................................ 2135
51. Présentation des mécanismes internes de PostgreSQL ........................................ 2141
51.1. Chemin d'une requête ........................................................................ 2141
51.2. Établissement des connexions ............................................................. 2141
51.3. Étape d'analyse ................................................................................ 2142
51.4. Système de règles de PostgreSQL ........................................................ 2143
51.5. Planificateur/Optimiseur ..................................................................... 2143
51.6. Exécuteur ........................................................................................ 2145
52. Catalogues système ..................................................................................... 2146
52.1. Aperçu ............................................................................................ 2146
52.2. pg_aggregate ............................................................................. 2148
52.3. pg_am ........................................................................................... 2149
52.4. pg_amop ....................................................................................... 2150
52.5. pg_amproc ................................................................................... 2151
52.6. pg_attrdef ................................................................................. 2151
52.7. pg_attribute ............................................................................. 2152

xv
Documentation PostgreSQL 11.22

52.8. pg_authid ................................................................................... 2154


52.9. pg_auth_members ....................................................................... 2155
52.10. pg_cast ...................................................................................... 2156
52.11. pg_class .................................................................................... 2157
52.12. pg_event_trigger .................................................................... 2160
52.13. pg_collation ............................................................................ 2160
52.14. pg_constraint .......................................................................... 2162
52.15. pg_conversion .......................................................................... 2164
52.16. pg_database .............................................................................. 2164
52.17. pg_db_role_setting ................................................................ 2165
52.18. pg_default_acl ........................................................................ 2166
52.19. pg_depend .................................................................................. 2167
52.20. pg_description ........................................................................ 2168
52.21. pg_enum ...................................................................................... 2169
52.22. pg_extension ............................................................................ 2169
52.23. pg_foreign_data_wrapper ...................................................... 2170
52.24. pg_foreign_server .................................................................. 2171
52.25. pg_foreign_table .................................................................... 2172
52.26. pg_index .................................................................................... 2172
52.27. pg_inherits .............................................................................. 2174
52.28. pg_init_privs .......................................................................... 2174
52.29. pg_language .............................................................................. 2175
52.30. pg_largeobject ........................................................................ 2176
52.31. pg_largeobject_metadata ...................................................... 2176
52.32. pg_namespace ............................................................................ 2177
52.33. pg_opclass ................................................................................ 2177
52.34. pg_operator .............................................................................. 2178
52.35. pg_opfamily .............................................................................. 2178
52.36. pg_partitioned_table ............................................................ 2179
52.37. pg_pltemplate .......................................................................... 2180
52.38. pg_policy .................................................................................. 2181
52.39. pg_proc ...................................................................................... 2182
52.40. pg_publication ........................................................................ 2185
52.41. pg_publication_rel ................................................................ 2186
52.42. pg_range .................................................................................... 2186
52.43. pg_replication_origin .......................................................... 2187
52.44. pg_rewrite ................................................................................ 2187
52.45. pg_seclabel .............................................................................. 2188
52.46. pg_sequence .............................................................................. 2188
52.47. pg_shdepend .............................................................................. 2189
52.48. pg_shdescription .................................................................... 2190
52.49. pg_shseclabel .......................................................................... 2191
52.50. pg_statistic ............................................................................ 2191
52.51. pg_statistic_ext .................................................................... 2193
52.52. pg_subscription ...................................................................... 2194
52.53. pg_subscription_rel .............................................................. 2195
52.54. pg_tablespace .......................................................................... 2195
52.55. pg_transform ............................................................................ 2196
52.56. pg_trigger ................................................................................ 2196
52.57. pg_ts_config ............................................................................ 2198
52.58. pg_ts_config_map .................................................................... 2198
52.59. pg_ts_dict ................................................................................ 2199
52.60. pg_ts_parser ............................................................................ 2199
52.61. pg_ts_template ........................................................................ 2200
52.62. pg_type ...................................................................................... 2200
52.63. pg_user_mapping ...................................................................... 2205
52.64. Vues système ................................................................................. 2206
52.65. pg_available_extensions ...................................................... 2207

xvi
Documentation PostgreSQL 11.22

52.66. pg_available_extension_versions ...................................... 2207


52.67. pg_config .................................................................................. 2208
52.68. pg_cursors ................................................................................ 2208
52.69. pg_file_settings .................................................................... 2209
52.70. pg_group .................................................................................... 2210
52.71. pg_hba_file_rules .................................................................. 2210
52.72. pg_indexes ................................................................................ 2211
52.73. pg_locks .................................................................................... 2211
52.74. pg_matviews .............................................................................. 2214
52.75. pg_policies .............................................................................. 2215
52.76. pg_prepared_statements ........................................................ 2215
52.77. pg_prepared_xacts .................................................................. 2216
52.78. pg_publication_tables .......................................................... 2217
52.79. pg_replication_origin_status ............................................ 2217
52.80. pg_replication_slots ............................................................ 2217
52.81. pg_roles .................................................................................... 2219
52.82. pg_rules .................................................................................... 2220
52.83. pg_seclabels ............................................................................ 2220
52.84. pg_sequences ............................................................................ 2221
52.85. pg_settings .............................................................................. 2222
52.86. pg_shadow .................................................................................. 2224
52.87. pg_stats .................................................................................... 2225
52.88. pg_tables .................................................................................. 2226
52.89. pg_timezone_abbrevs .............................................................. 2227
52.90. pg_timezone_names .................................................................. 2227
52.91. pg_user ...................................................................................... 2228
52.92. pg_user_mappings .................................................................... 2228
52.93. pg_views .................................................................................... 2229
53. Protocole client/serveur ................................................................................ 2230
53.1. Aperçu ............................................................................................ 2230
53.2. Flux de messages ............................................................................. 2232
53.3. Protocole de réplication logique en flux ................................................ 2245
53.4. Types de données des messages .......................................................... 2246
53.5. Authentification SASL ...................................................................... 2246
53.6. Protocole de réplication en continu ...................................................... 2248
53.7. Formats de message .......................................................................... 2255
53.8. Champs des messages d'erreur et d'avertissement .................................... 2272
53.9. Formats des messages de la réplication logique ...................................... 2274
53.10. Résumé des modifications depuis le protocole 2.0 ................................. 2278
54. Conventions de codage pour PostgreSQL ........................................................ 2280
54.1. Formatage ....................................................................................... 2280
54.2. Reporter les erreurs dans le serveur ..................................................... 2281
54.3. Guide de style des messages d'erreurs .................................................. 2284
54.4. Conventions diverses de codage .......................................................... 2288
55. Support natif des langues ............................................................................. 2291
55.1. Pour le traducteur ............................................................................. 2291
55.2. Pour le développeur .......................................................................... 2294
56. Écrire un gestionnaire de langage procédural ................................................... 2297
57. Écrire un wrapper de données distantes ........................................................... 2300
57.1. Fonctions d'un wrapper de données distantes ......................................... 2300
57.2. Routines callback des wrappers de données distantes ............................... 2300
57.3. Fonctions d'aide pour les wrapper de données distantes ............................ 2316
57.4. Planification de la requête avec un wrapper de données distantes ............... 2317
57.5. Le verrouillage de ligne dans les wrappers de données distantes ................ 2319
58. Écrire une méthode d'échantillonnage de table .................................................. 2322
58.1. Fonctions de support d'une méthode d'échantillonnage ............................. 2323
59. Écrire un module de parcours personnalisé ...................................................... 2326
59.1. Créer des parcours de chemin personnalisés .......................................... 2326

xvii
Documentation PostgreSQL 11.22

59.2. Créer des parcours de plans personnalisés ............................................. 2328


59.3. Exécution de parcours personnalisés .................................................... 2329
60. Optimiseur génétique de requêtes (Genetic Query Optimizer) .............................. 2332
60.1. Gérer les requêtes, un problème d'optimisation complexe ......................... 2332
60.2. Algorithmes génétiques ..................................................................... 2332
60.3. Optimisation génétique des requêtes (GEQO) dans PostgreSQL ................. 2333
60.4. Lectures supplémentaires ................................................................... 2335
61. Définition de l'interface des méthodes d'accès aux index .................................... 2336
61.1. Structure basique de l'API pour les index .............................................. 2336
61.2. Fonctions des méthode d'accès aux index .............................................. 2339
61.3. Parcours d'index ............................................................................... 2344
61.4. Considérations sur le verrouillage d'index ............................................. 2346
61.5. Vérification de l'unicité par les index ................................................... 2347
61.6. Fonctions d'estimation des coûts d'index ............................................... 2348
62. Enregistrements génériques des journaux de transactions .................................... 2352
63. Index B-Tree .............................................................................................. 2354
63.1. Introduction ..................................................................................... 2354
63.2. Comportement des classes d'opérateur B-Tree ........................................ 2354
63.3. Fonctions de support B-Tree ............................................................... 2355
63.4. Implémentation ................................................................................ 2357
64. Index GiST ................................................................................................ 2358
64.1. Introduction ..................................................................................... 2358
64.2. Classes d'opérateur internes ................................................................ 2358
64.3. Extensibilité ..................................................................................... 2359
64.4. Implémentation ................................................................................ 2369
64.5. Exemples ........................................................................................ 2369
65. Index SP-GiST ........................................................................................... 2371
65.1. Introduction ..................................................................................... 2371
65.2. Classes d'opérateur internes ................................................................ 2371
65.3. Extensibilité ..................................................................................... 2372
65.4. Implémentation ................................................................................ 2380
65.5. Exemples ........................................................................................ 2382
66. Index GIN ................................................................................................. 2383
66.1. Introduction ..................................................................................... 2383
66.2. Classes d'opérateur internes ................................................................ 2383
66.3. Extensibilité ..................................................................................... 2383
66.4. Implantation .................................................................................... 2386
66.5. Conseils et astuces GIN ..................................................................... 2387
66.6. Limitations ...................................................................................... 2388
66.7. Exemples ........................................................................................ 2388
67. Index BRIN ............................................................................................... 2390
67.1. Introduction ..................................................................................... 2390
67.2. Opérateurs de classe intégrés .............................................................. 2391
67.3. Extensibilité ..................................................................................... 2392
68. Index Hash ................................................................................................ 2396
68.1. Aperçu ............................................................................................ 2396
68.2. Implémentation ................................................................................ 2397
69. Stockage physique de la base de données ........................................................ 2398
69.1. Emplacement des fichiers de la base de données ..................................... 2398
69.2. TOAST ........................................................................................... 2400
69.3. Carte des espaces libres ..................................................................... 2403
69.4. Carte de visibilité ............................................................................. 2403
69.5. Fichier d'initialisation ........................................................................ 2404
69.6. Emplacement des pages de la base de données ....................................... 2404
69.7. Heap-Only Tuples (HOT) .................................................................. 2407
70. Déclaration du catalogue système et contenu initial ........................................... 2408
70.1. Règles de déclaration de catalogue système ........................................... 2408
70.2. Données initiales du catalogue système ................................................ 2409

xviii
Documentation PostgreSQL 11.22

70.3. Format des fichiers BKI .................................................................... 2414


70.4. Commandes BKI .............................................................................. 2414
70.5. Structure du fichier BKI de « bootstrap » .............................................. 2415
70.6. Exemple BKI ................................................................................... 2416
71. Comment le planificateur utilise les statistiques ................................................ 2417
71.1. Exemples d'estimation des lignes ......................................................... 2417
71.2. Exemples de statistiques multivariées ................................................... 2423
71.3. Statistiques de l'optimiseur et sécurité .................................................. 2425
VIII. Annexes .......................................................................................................... 2426
A. Codes d'erreurs de PostgreSQL ...................................................................... 2433
B. Support de date/heure ................................................................................... 2441
B.1. Interprétation des Date/Heure saisies ..................................................... 2441
B.2. Gestion des horodatages ambigus ou invalides ........................................ 2442
B.3. Mots clés Date/Heure ......................................................................... 2443
B.4. Fichiers de configuration date/heure ...................................................... 2444
B.5. Spécification POSIX des fuseaux horaires .............................................. 2445
B.6. Histoire des unités ............................................................................. 2448
B.7. Dates Julien ...................................................................................... 2448
C. Mots-clé SQL .............................................................................................. 2450
D. Conformité SQL .......................................................................................... 2478
D.1. Fonctionnalités supportées ................................................................... 2479
D.2. Fonctionnalités non supportées ............................................................. 2491
D.3. Limites XML et conformité au SQL/XML ............................................. 2499
E. Notes de version .......................................................................................... 2504
E.1. Release 11.22 .................................................................................... 2504
E.2. Release 11.21 .................................................................................... 2507
E.3. Release 11.20 .................................................................................... 2510
E.4. Release 11.19 .................................................................................... 2514
E.5. Release 11.18 .................................................................................... 2517
E.6. Release 11.17 .................................................................................... 2521
E.7. Release 11.16 .................................................................................... 2524
E.8. Release 11.15 .................................................................................... 2527
E.9. Release 11.14 .................................................................................... 2530
E.10. Release 11.13 .................................................................................. 2536
E.11. Release 11.12 .................................................................................. 2541
E.12. Release 11.11 .................................................................................. 2544
E.13. Release 11.10 .................................................................................. 2549
E.14. Release 11.9 .................................................................................... 2553
E.15. Release 11.8 .................................................................................... 2558
E.16. Release 11.7 .................................................................................... 2562
E.17. Release 11.6 .................................................................................... 2566
E.18. Release 11.5 .................................................................................... 2571
E.19. Release 11.4 .................................................................................... 2575
E.20. Release 11.3 .................................................................................... 2577
E.21. Release 11.2 .................................................................................... 2582
E.22. Release 11.1 .................................................................................... 2588
E.23. Release 11 ...................................................................................... 2590
E.24. Versions précédentes ........................................................................ 2609
F. Modules supplémentaires fournis ..................................................................... 2610
F.1. adminpack ........................................................................................ 2611
F.2. amcheck ........................................................................................... 2612
F.3. auth_delay ........................................................................................ 2615
F.4. auto_explain ...................................................................................... 2616
F.5. bloom .............................................................................................. 2618
F.6. btree_gin .......................................................................................... 2622
F.7. btree_gist .......................................................................................... 2622
F.8. citext ............................................................................................... 2623
F.9. cube ................................................................................................. 2626

xix
Documentation PostgreSQL 11.22

F.10. dblink ............................................................................................. 2631


F.11. dict_int ........................................................................................... 2663
F.12. dict_xsyn ........................................................................................ 2664
F.13. earthdistance .................................................................................... 2665
F.14. file_fdw .......................................................................................... 2667
F.15. fuzzystrmatch .................................................................................. 2670
F.16. hstore ............................................................................................. 2672
F.17. intagg ............................................................................................. 2680
F.18. intarray ........................................................................................... 2681
F.19. isn ................................................................................................. 2684
F.20. lo ................................................................................................... 2688
F.21. ltree ............................................................................................... 2689
F.22. pageinspect ...................................................................................... 2697
F.23. passwordcheck ................................................................................. 2705
F.24. pg_buffercache ................................................................................. 2705
F.25. pgcrypto ......................................................................................... 2707
F.26. pg_freespacemap .............................................................................. 2719
F.27. pg_prewarm .................................................................................... 2721
F.28. pgrowlocks ...................................................................................... 2722
F.29. pg_stat_statements ............................................................................ 2724
F.30. pgstattuple ....................................................................................... 2729
F.31. pg_trgm .......................................................................................... 2733
F.32. pg_visibility .................................................................................... 2739
F.33. postgres_fdw ................................................................................... 2741
F.34. seg ................................................................................................. 2747
F.35. sepgsql ........................................................................................... 2750
F.36. spi ................................................................................................. 2759
F.37. sslinfo ............................................................................................ 2761
F.38. tablefunc ......................................................................................... 2763
F.39. tcn ................................................................................................. 2774
F.40. test_decoding ................................................................................... 2775
F.41. tsm_system_rows ............................................................................. 2775
F.42. tsm_system_time .............................................................................. 2776
F.43. unaccent ......................................................................................... 2776
F.44. uuid-ossp ........................................................................................ 2778
F.45. xml2 .............................................................................................. 2780
G. Programmes supplémentaires fournis ............................................................... 2785
G.1. Applications clients ............................................................................ 2785
G.2. Applications serveurs ......................................................................... 2792
H. Projets externes ........................................................................................... 2797
H.1. Interfaces client ................................................................................. 2797
H.2. Outils d'administration ........................................................................ 2797
H.3. Langages procéduraux ........................................................................ 2797
H.4. Extensions ........................................................................................ 2797
I. Dépôt du code source .................................................................................... 2798
I.1. Récupérer les sources via Git ................................................................ 2798
J. Documentation ............................................................................................. 2799
J.1. DocBook ........................................................................................... 2799
J.2. Ensemble d'outils ................................................................................ 2799
J.3. Construire la documentation ................................................................. 2802
J.4. Écriture de la documentation ................................................................ 2804
J.5. Guide des styles ................................................................................. 2804
K. Acronymes ................................................................................................. 2807
L. Fonctionnalités obsolètes ou renommées ........................................................... 2813
L.1. pg_xlogdump renommé en pg_waldump .......................................... 2813
L.2. pg_resetxlog renommé en pg_resetwal ...................................... 2813
L.3. pg_receivexlog renommé en pg_receivewal .............................. 2813
M. Traduction française ..................................................................................... 2814

xx
Documentation PostgreSQL 11.22

Bibliographie ........................................................................................................... 2816


Index ...................................................................................................................... 2818

xxi
Liste des illustrations
60.1. Diagramme structuré d'un algorithme génétique ..................................................... 2333

xxii
Liste des tableaux
4.1. Séquences d'échappements avec antislash .................................................................. 36
4.2. Précédence des opérateurs (du plus haut vers le plus bas) ............................................. 41
8.1. Types de données ................................................................................................ 140
8.2. Types numériques ................................................................................................ 142
8.3. Types monétaires ................................................................................................. 147
8.4. Types caractère ................................................................................................... 147
8.5. Types caractères spéciaux ..................................................................................... 149
8.6. Types de données binaires ..................................................................................... 149
8.7. Octets littéraux bytea à échapper ......................................................................... 151
8.8. Octets échappés en sortie pour bytea .................................................................... 151
8.9. Types date et heure .............................................................................................. 152
8.10. Saisie de date .................................................................................................... 153
8.11. Saisie d'heure .................................................................................................... 154
8.12. Saisie des fuseaux horaires .................................................................................. 155
8.13. Saisie de dates/heures spéciales ............................................................................ 156
8.14. Styles d'affichage de date/heure ............................................................................ 157
8.15. Convention de présentation des dates ..................................................................... 157
8.16. Abréviations d'unités d'intervalle ISO 8601 ............................................................. 160
8.17. Saisie d'intervalle ............................................................................................... 161
8.18. Exemples de styles d'affichage d'intervalles ............................................................ 162
8.19. Type de données booléen .................................................................................... 162
8.20. Types géométriques ............................................................................................ 165
8.21. Types d'adresses réseau ....................................................................................... 168
8.22. Exemples de saisie de types cidr ........................................................................ 168
8.23. Types primitifs JSON et types PostgreSQL correspondants ........................................ 178
8.24. Types identifiant d'objet ...................................................................................... 208
8.25. Pseudo-Types .................................................................................................... 210
9.1. Opérateurs de comparaison .................................................................................... 211
9.2. Prédicats de comparaison ...................................................................................... 212
9.3. Fonctions de comparaison ..................................................................................... 214
9.4. Opérateurs mathématiques ..................................................................................... 214
9.5. Fonctions mathématiques ...................................................................................... 215
9.6. Fonctions de génération de nombres aléatoires .......................................................... 217
9.7. Fonctions trigonométriques .................................................................................... 217
9.8. Fonctions et opérateurs SQL pour le type chaîne ....................................................... 218
9.9. Autres fonctions de chaîne .................................................................................... 219
9.10. Conversions intégrées ......................................................................................... 226
9.11. Fonctions et opérateurs SQL pour chaînes binaires ................................................... 232
9.12. Autres fonctions sur les chaînes binaires ................................................................ 233
9.13. Opérateurs sur les chaînes de bits ......................................................................... 235
9.14. Opérateurs de correspondance des expressions rationnelles ........................................ 238
9.15. Atomes d'expressions rationnelles ......................................................................... 242
9.16. quantificateur d'expressions rationnelles ................................................................. 243
9.17. Contraintes des expressions rationnelles ................................................................. 244
9.18. Échappements de caractère dans les expressions rationnelles ...................................... 245
9.19. Échappement de raccourcis de classes dans les expressions rationnelles ........................ 246
9.20. Échappements de contrainte dans les expressions rationnelles ..................................... 246
9.21. Rétroréférences dans les expressions rationnelles ..................................................... 247
9.22. Lettres d'option intégrées à une ERA ..................................................................... 247
9.23. Fonctions de formatage ....................................................................................... 251
9.24. Modèles pour le formatage de champs de type date/heure .......................................... 252
9.25. Modificateurs de motifs pour le formatage des dates/heures ....................................... 254
9.26. Motifs de modèle pour le formatage de valeurs numériques ....................................... 256
9.27. Modifications de motifs pour le formatage numérique ............................................... 257
9.28. Exemples avec to_char .................................................................................... 257

xxiii
Documentation PostgreSQL 11.22

9.29. Opérateurs date/heure ......................................................................................... 259


9.30. Fonctions date/heure ........................................................................................... 259
9.31. Variantes AT TIME ZONE ................................................................................ 269
9.32. Fonctions de support enum .................................................................................. 272
9.33. Opérateurs géométriques ..................................................................................... 273
9.34. Fonctions géométriques ....................................................................................... 274
9.35. Fonctions de conversion de types géométriques ....................................................... 275
9.36. Opérateurs cidr et inet ................................................................................... 277
9.37. Fonctions cidr et inet .................................................................................... 278
9.38. Fonctions macaddr ........................................................................................... 279
9.39. macaddr8 Fonctions ......................................................................................... 279
9.40. Opérateurs de recherche plein texte ....................................................................... 279
9.41. Fonctions de la recherche plein texte ..................................................................... 280
9.42. Fonctions de débogage de la recherche plein texte .................................................... 285
9.43. Opérateurs json et jsonb ................................................................................. 300
9.44. Opérateurs jsonb supplémentaires ....................................................................... 301
9.45. Fonctions de création de données JSON ................................................................. 303
9.46. Fonctions de traitement du JSON .......................................................................... 305
9.47. Fonctions séquence ............................................................................................. 310
9.48. Opérateurs pour les tableaux ................................................................................ 315
9.49. Fonctions pour les tableaux .................................................................................. 317
9.50. Opérateurs pour les types range ............................................................................ 319
9.51. Fonctions range ................................................................................................. 321
9.52. Fonctions d'agrégat générales ............................................................................... 321
9.53. Fonctions d'agrégats pour les statistiques ................................................................ 324
9.54. Fonctions d'agrégat par ensemble trié .................................................................... 326
9.55. Fonctions d'agrégat par ensemble hypothétique ........................................................ 328
9.56. Opérations de regroupement ................................................................................. 328
9.57. Fonctions Window généralistes ............................................................................. 329
9.58. Fonctions de génération de séries .......................................................................... 337
9.59. Fonctions de génération d'indices .......................................................................... 338
9.60. Fonctions d'information de session ........................................................................ 340
9.61. Fonctions de consultation des privilèges d'accès ....................................................... 343
9.62. Fonctions d'interrogation de visibilité dans les schémas ............................................. 346
9.63. Fonctions d'information du catalogue système ......................................................... 347
9.64. Propriétés des colonnes d'index ............................................................................ 351
9.65. Propriétés des index ........................................................................................... 351
9.66. Propriétés des méthodes d'accès aux index .............................................................. 351
9.67. Fonctions d'information et d'adressage des objets ..................................................... 353
9.68. Fonctions d'informations sur les commentaires ........................................................ 354
9.69. ID de transaction et instantanés ............................................................................ 354
9.70. Composants de l'instantané .................................................................................. 355
9.71. Informations sur les transactions validées ............................................................... 356
9.72. Fonctions des données de contrôle ........................................................................ 356
9.73. Colonnes de pg_control_checkpoint ........................................................... 356
9.74. Colonnes de pg_control_system ................................................................... 357
9.75. Colonnes de pg_control_init ....................................................................... 357
9.76. Colonnes de pg_control_recovery ............................................................... 357
9.77. Fonctions agissant sur les paramètres de configuration .............................................. 358
9.78. Fonctions d'envoi de signal au serveur ................................................................... 358
9.79. Fonctions de contrôle de la sauvegarde .................................................................. 359
9.80. Fonctions d'information sur la restauration .............................................................. 362
9.81. Fonctions de contrôle de la restauration ................................................................. 363
9.82. Fonction de synchronisation de snapshot ................................................................ 364
9.83. Fonctions SQL pour la réplication ......................................................................... 365
9.84. Fonctions de calcul de la taille des objets de la base de données .................................. 369
9.85. Fonctions de récupération de l'emplacement des objets de la base de données ................ 371
9.86. Fonctions de gestion des collations ........................................................................ 372

xxiv
Documentation PostgreSQL 11.22

9.87. Fonctions de maintenance des index ...................................................................... 372


9.88. Fonctions d'accès générique aux fichiers ................................................................ 373
9.89. Fonctions de verrous consultatifs .......................................................................... 375
9.90. Table Rewrite information ................................................................................... 380
12.1. Types de jeton de l'analyseur par défaut ................................................................. 431
13.1. Niveaux d'isolation des transactions ....................................................................... 456
13.2. Modes de verrou conflictuels ............................................................................... 464
13.3. Verrous en conflit au niveau ligne ......................................................................... 465
18.1. Paramètres system v ipc ...................................................................................... 546
18.2. Utilisation des fichiers serveur SSL ....................................................................... 563
19.1. Modes pour synchronous_commit ......................................................................... 588
19.2. Niveaux de sévérité des messages ......................................................................... 610
19.3. Clé d'option courte ............................................................................................. 640
21.1. Rôles par défaut ................................................................................................. 669
23.1. Jeux de caractères de PostgreSQL ......................................................................... 687
23.2. Conversion de jeux de caractères client/serveur ....................................................... 690
26.1. Matrice de fonctionnalités : haute disponibilité, répartition de charge et réplication ......... 724
28.1. Vues statistiques dynamiques ............................................................................... 754
28.2. Vues sur les statistiques récupérées ....................................................................... 754
28.3. Vue pg_stat_activity ................................................................................. 756
28.4. Description de wait_event .............................................................................. 760
28.5. Vue pg_stat_replication ........................................................................... 772
28.6. Vue pg_stat_wal_receiver ......................................................................... 776
28.7. Vue pg_stat_subscription ......................................................................... 777
28.8. Vue pg_stat_ssl .......................................................................................... 778
28.9. Vue pg_stat_archiver ................................................................................. 778
28.10. Vue pg_stat_bgwriter ............................................................................... 779
28.11. Vue pg_stat_database ............................................................................... 779
28.12. Vue pg_stat_database_conflicts ........................................................... 781
28.13. Vue pg_stat_all_tables ........................................................................... 782
28.14. Vue pg_stat_all_indexes ......................................................................... 783
28.15. Vue pg_statio_all_tables ....................................................................... 784
28.16. Vue pg_statio_all_indexes ..................................................................... 785
28.17. Vue pg_statio_all_sequences ................................................................. 785
28.18. Vue pg_stat_user_functions ................................................................... 785
28.19. Fonctions supplémentaires de statistiques .............................................................. 786
28.20. Fonctions statistiques par processus serveur .......................................................... 788
28.21. Vue pg_stat_progress_vacuum ................................................................. 789
28.22. Phases du VACUUM ........................................................................................ 790
28.23. Sondes disponibles pour DTrace ......................................................................... 791
28.24. Types définis utilisés comme paramètres de sonde .................................................. 799
34.1. Description des modes SSL ................................................................................. 909
34.2. Utilisation des fichiers SSL libpq/client .................................................................. 910
35.1. Fonctions SQL pour les Large Objects ................................................................... 930
36.1. Correspondance Entre les Types PostgreSQL et les Types de Variables C ..................... 946
36.2. Formats d'Entrée Valides pour PGTYPESdate_from_asc ..................................... 965
36.3. Formats d'Entrée Valides pour PGTYPESdate_fmt_asc ....................................... 968
36.4. Formats d'Entrée Valides pour rdefmtdate ......................................................... 968
36.5. Formats d'Entrée Valide pour PGTYPEStimestamp_from_asc ............................. 969
37.1. Colonnes de information_schema_catalog_name ...................................... 1052
37.2. Colonnes de administrable_role_authorizations .................................. 1052
37.3. Colonnes de applicable_roles .................................................................... 1053
37.4. Colonnes de attributes ................................................................................ 1053
37.5. Colonnes de character_sets ........................................................................ 1057
37.6. Colonnes de check_constraint_routine_usage ........................................ 1058
37.7. Colonnes de check_constraints .................................................................. 1058
37.8. Colonnes de collations ................................................................................ 1058
37.9. Colonnes de collation_character_set_applicability .......................... 1059

xxv
Documentation PostgreSQL 11.22

37.10. Colonnes de column_domain_usage ............................................................ 1059


37.11. Colonnes de column_options ...................................................................... 1060
37.12. Colonnes de column_privileges ................................................................ 1060
37.13. Colonnes de column_udt_usage .................................................................. 1060
37.14. Colonnes de columns .................................................................................... 1061
37.15. Colonnes de constraint_column_usage .................................................... 1065
37.16. Colonnes de constraint_table_usage ...................................................... 1065
37.17. Colonnes de data_type_privileges .......................................................... 1066
37.18. Colonnes de domain_constraints .............................................................. 1066
37.19. Colonnes de domain_udt_usage .................................................................. 1067
37.20. Colonnes de domains .................................................................................... 1067
37.21. Colonnes de element_types ........................................................................ 1070
37.22. Colonnes de enabled_roles ........................................................................ 1072
37.23. Colonnes de foreign_data_wrapper_options .......................................... 1072
37.24. Colonnes de foreign_data_wrappers ........................................................ 1072
37.25. Colonnes de foreign_server_options ...................................................... 1073
37.26. Colonnes de foreign_servers .................................................................... 1073
37.27. Colonnes de foreign_table_options ........................................................ 1074
37.28. Colonnes de foreign_tables ...................................................................... 1074
37.29. Colonnes de key_column_usage .................................................................. 1075
37.30. Colonnes de parameters .............................................................................. 1075
37.31. Colonnes de referential_constraints .................................................... 1077
37.32. Colonnes de role_column_grants .............................................................. 1078
37.33. Colonnes de role_routine_grants ............................................................ 1078
37.34. Colonnes de role_table_grants ................................................................ 1079
37.35. Colonnes de role_udt_grants .................................................................... 1080
37.36. Colonnes de role_usage_grants ................................................................ 1080
37.37. Colonnes de routine_privileges .............................................................. 1081
37.38. Colonnes de routines .................................................................................. 1081
37.39. Colonnes de schemata .................................................................................. 1086
37.40. Colonnes de sequences ................................................................................ 1086
37.41. Colonnes de sql_features .......................................................................... 1087
37.42. Colonnes de sql_implementation_info .................................................... 1088
37.43. Colonnes de sql_languages ........................................................................ 1088
37.44. Colonnes de sql_packages .......................................................................... 1089
37.45. Colonnes de sql_parts ................................................................................ 1089
37.46. Colonnes de sql_sizing .............................................................................. 1090
37.47. Colonnes de sql_sizing_profiles ............................................................ 1090
37.48. Colonnes de table_constraints ................................................................ 1091
37.49. Colonnes de table_privileges .................................................................. 1091
37.50. Colonnes de tables ...................................................................................... 1092
37.51. Colonnes de transforms .............................................................................. 1092
37.52. Colonnes de triggered_update_columns .................................................. 1093
37.53. Colonnes de triggers .................................................................................. 1094
37.54. Colonnes de udt_privileges ...................................................................... 1095
37.55. Colonnes de usage_privileges .................................................................. 1096
37.56. Colonnes de user_defined_types .............................................................. 1097
37.57. Colonnes de user_mapping_options .......................................................... 1098
37.58. Colonnes de user_mappings ........................................................................ 1099
37.59. Colonnes de view_column_usage ................................................................ 1099
37.60. Colonnes de view_routine_usage .............................................................. 1100
37.61. Colonnes de view_table_usage .................................................................. 1100
37.62. Colonnes de views ........................................................................................ 1101
38.1. Équivalence des types C et des types SQL intégrés ................................................. 1133
38.2. Stratégies B-tree ............................................................................................... 1170
38.3. Stratégies de découpage ..................................................................................... 1171
38.4. Stratégies « R-tree » pour GiST à deux dimensions ................................................. 1171
38.5. Stratégies point SP-GiST ................................................................................... 1171

xxvi
Documentation PostgreSQL 11.22

38.6. Stratégies des tableaux GIN ............................................................................... 1172


38.7. Stratégies MinMax pour BRIN ........................................................................... 1172
38.8. Fonctions d'appui de B-tree ................................................................................ 1172
38.9. Fonctions d'appui pour découpage ....................................................................... 1173
38.10. Fonctions d'appui pour GiST ............................................................................ 1173
38.11. Fonctions de support SP-GiST .......................................................................... 1174
38.12. Fonctions d'appui GIN ..................................................................................... 1174
38.13. Fonctions de support BRIN .............................................................................. 1175
40.1. Support des triggers sur événement par commande ................................................. 1208
43.1. Éléments de diagnostique disponibles ................................................................... 1268
43.2. Diagnostiques et erreurs ..................................................................................... 1283
241. Politiques appliquées par type de commande .......................................................... 1665
242. Variables automatiques ....................................................................................... 1985
243. Opérateurs pgbench par priorité croissante ............................................................. 1987
244. Fonctions pgbench ............................................................................................. 1987
52.1. Catalogues système ........................................................................................... 2146
52.2. Les colonnes de pg_aggregate ....................................................................... 2148
52.3. Colonnes de pg_am ......................................................................................... 2150
52.4. Colonnes de pg_amop ..................................................................................... 2150
52.5. Colonnes de pg_amproc .................................................................................. 2151
52.6. Colonnes de pg_attrdef ................................................................................ 2152
52.7. Colonnes de pg_attribute ............................................................................ 2152
52.8. Colonnes de pg_authid .................................................................................. 2155
52.9. Colonnes de pg_auth_members ...................................................................... 2156
52.10. Colonnes de pg_cast .................................................................................... 2156
52.11. Colonnes de pg_class .................................................................................. 2157
52.12. Colonnes de pg_event_trigger .................................................................. 2160
52.13. Colonnes de pg_collation .......................................................................... 2161
52.14. Colonnes de pg_constraint ........................................................................ 2162
52.15. Colonnes de pg_conversion ........................................................................ 2164
52.16. Colonnes de pg_database ............................................................................ 2164
52.17. Colonnes de pg_db_role_setting .............................................................. 2166
52.18. Colonnes de pg_default_acl ...................................................................... 2166
52.19. Colonnes de pg_depend ................................................................................ 2167
52.20. Colonnes de pg_description ...................................................................... 2169
52.21. Colonnes de pg_enum .................................................................................... 2169
52.22. Colonnes de pg_extension .......................................................................... 2169
52.23. Colonnes de pg_foreign_data_wrapper .................................................... 2170
52.24. Colonnes de pg_foreign_server ................................................................ 2171
52.25. Colonnes de pg_foreign_table .................................................................. 2172
52.26. Colonnes de pg_index .................................................................................. 2172
52.27. Colonnes de pg_inherits ............................................................................ 2174
52.28. Colonnes de pg_init_privs ........................................................................ 2175
52.29. Colonnes de pg_language ............................................................................ 2175
52.30. Colonnes de pg_largeobject ...................................................................... 2176
52.31. Colonnes de pg_largeobject_metadata .................................................... 2177
52.32. Colonnes de pg_namespace .......................................................................... 2177
52.33. Colonnes de pg_opclass .............................................................................. 2177
52.34. Colonnes de pg_operator ............................................................................ 2178
52.35. Colonnes de pg_opfamily ............................................................................ 2179
52.36. Colonnes de pg_partitioned_table .......................................................... 2179
52.37. Colonnes de pg_pltemplate ........................................................................ 2181
52.38. Colonnes de pg_policy ................................................................................ 2181
52.39. Colonnes de pg_proc .................................................................................... 2182
52.40. Colonnes de pg_publication ...................................................................... 2185
52.41. Colonnes de pg_publication_rel .............................................................. 2186
52.42. Colonnes de pg_range .................................................................................. 2186
52.43. Colonnes de pg_replication_origin ........................................................ 2187

xxvii
Documentation PostgreSQL 11.22

52.44. Colonnes de pg_rewrite .............................................................................. 2187


52.45. Colonnes de pg_seclabel ............................................................................ 2188
52.46. Colonnes de pg_sequence ............................................................................ 2189
52.47. Colonnes de pg_shdepend ............................................................................ 2189
52.48. Colonnes de pg_shdescription .................................................................. 2190
52.49. Colonnes de pg_shseclabel ........................................................................ 2191
52.50. Colonnes de pg_statistic .......................................................................... 2192
52.51. Colonnes de pg_statistic_ext .................................................................. 2193
52.52. Colonnes de pg_subscription .................................................................... 2194
52.53. Colonnes de pg_subscription_rel ............................................................ 2195
52.54. Colonnes de pg_tablespace ........................................................................ 2195
52.55. Colonnes de pg_transform .......................................................................... 2196
52.56. Colonnes de pg_trigger .............................................................................. 2196
52.57. Colonnes de pg_ts_config .......................................................................... 2198
52.58. Colonnes de pg_ts_config_map .................................................................. 2198
52.59. Colonnes de pg_ts_dict .............................................................................. 2199
52.60. Colonnes de pg_ts_parser .......................................................................... 2199
52.61. Colonnes de pg_ts_template ...................................................................... 2200
52.62. Colonnes de pg_type .................................................................................... 2200
52.63. Codes typcategory .................................................................................... 2204
52.64. Colonnes de pg_user_mapping .................................................................... 2205
52.65. Vues système ................................................................................................. 2206
52.66. Colonnes de pg_available_extensions .................................................... 2207
52.67. Colonnes de pg_available_extension_versions .................................... 2207
52.68. Colonnes de pg_config ................................................................................ 2208
52.69. Colonnes de pg_cursors .............................................................................. 2209
52.70. Colonnes de pg_file_settings .................................................................. 2209
52.71. Colonnes de pg_group .................................................................................. 2210
52.72. Colonnes de pg_hba_file_rules ................................................................ 2210
52.73. Colonnes de pg_indexes .............................................................................. 2211
52.74. Colonnes de pg_locks .................................................................................. 2212
52.75. Colonnes de pg_matviews ............................................................................ 2214
52.76. Colonnes de pg_policies ............................................................................ 2215
52.77. Colonnes de pg_prepared_statements ...................................................... 2216
52.78. Colonnes de pg_prepared_xacts ................................................................ 2216
52.79. Colonnes de pg_publication_tables ........................................................ 2217
52.80. Colonnes de pg_replication_origin_status .......................................... 2217
52.81. Colonnes de pg_replication_slots .......................................................... 2218
52.82. Colonnes de pg_roles .................................................................................. 2219
52.83. Colonnes de pg_rules .................................................................................. 2220
52.84. Colonnes de pg_seclabels .......................................................................... 2221
52.85. Colonnes de pg_sequences .......................................................................... 2221
52.86. Colonnes de pg_settings ............................................................................ 2222
52.87. Colonnes de pg_shadow ................................................................................ 2224
52.88. Colonnes de pg_stats .................................................................................. 2225
52.89. Colonnes de pg_tables ................................................................................ 2226
52.90. Colonnes de pg_timezone_abbrevs ............................................................ 2227
52.91. Colonnes de pg_timezone_names ................................................................ 2227
52.92. Colonnes de pg_user .................................................................................... 2228
52.93. Colonnes de pg_user_mappings .................................................................. 2228
52.94. Colonnes de pg_views .................................................................................. 2229
64.1. Classes d'opérateur GiST internes ........................................................................ 2358
65.1. Classes d'opérateur SP-GiST internes ................................................................... 2371
66.1. Classes d'opérateur GIN internes ......................................................................... 2383
67.1. Classe d'opérateur BRIN intégrée ........................................................................ 2391
67.2. Fonctions et numéros de support pour les classes d'opérateur Minmax ........................ 2393
67.3. Fonctions et numéros de support pour les classes d'opérateur d'inclusion ..................... 2393
69.1. Contenu de PGDATA ......................................................................................... 2398

xxviii
Documentation PostgreSQL 11.22

69.2. Disposition d'une page ...................................................................................... 2404


69.3. Disposition de PageHeaderData .......................................................................... 2405
69.4. Disposition de HeapTupleHeaderData .................................................................. 2406
A.1. Codes d'erreur de PostgreSQL ............................................................................. 2433
B.1. Noms de mois ................................................................................................... 2443
B.2. Noms des jours de la semaine .............................................................................. 2443
B.3. Modificateurs de Champs Date/Heure ................................................................... 2444
C.1. Mots-clé SQL ................................................................................................... 2450
F.1. Fonctions de adminpack .................................................................................. 2611
F.2. Représentations externes d'un cube ....................................................................... 2626
F.3. Opérateurs pour cube .......................................................................................... 2627
F.4. Fonctions cube .................................................................................................. 2628
F.5. Fonctions earthdistance par cubes ......................................................................... 2666
F.6. Opérateurs earthdistance par points ....................................................................... 2667
F.7. Opérateurshstore ............................................................................................ 2673
F.8. Fonctions hstore ............................................................................................. 2674
F.9. Fonctions intarray ......................................................................................... 2682
F.10. Opérateurs d'intarray ................................................................................... 2682
F.11. Types de données isn ...................................................................................... 2685
F.12. Fonctions de isn ............................................................................................. 2686
F.13. Opérateurs ltree ............................................................................................ 2691
F.14. Fonctions ltree ............................................................................................. 2693
F.15. Colonnes de pg_buffercache ....................................................................... 2706
F.16. Algorithmes supportés par crypt() .................................................................. 2708
F.17. Nombre d'itération pour crypt() ...................................................................... 2709
F.18. Vitesse de l'algorithm de hachage ........................................................................ 2709
F.19. Résumé de fonctionnalités avec et sans OpenSSL .................................................. 2717
F.20. Colonnes de pgrowlocks ............................................................................... 2722
F.21. Colonnes de pg_stat_statements ................................................................ 2724
F.22. Colonnes de pgstattuple ............................................................................. 2729
F.23. Colonnes de pgstattuple_approx ................................................................ 2733
F.24. Fonctions de pg_trgm ..................................................................................... 2734
F.25. Opérateurs de pg_trgm ................................................................................... 2736
F.26. Représentations externes de seg ......................................................................... 2748
F.27. Exemples d'entrées valides de type seg ............................................................... 2748
F.28. Opérateurs GiST du type Seg ............................................................................. 2749
F.29. Fonctions Sepgsql ............................................................................................ 2757
F.30. Fonctions tablefunc ..................................................................................... 2763
F.31. Paramètres connectby ................................................................................... 2771
F.32. Fonctions pour la génération d'UUID ................................................................... 2779
F.33. Fonctions renvoyant des constantes UUID ............................................................ 2779
F.34. Fonctions ........................................................................................................ 2780
F.35. Paramètres de xpath_table ........................................................................... 2782

xxix
Liste des exemples
8.1. Utilisation des types caractère ................................................................................ 149
8.2. Utilisation du type boolean. ................................................................................ 163
8.3. Utiliser les types de chaînes de bits ......................................................................... 171
9.1. Feuille de style XSLT pour convertir du SQL/XML en HTML ..................................... 299
10.1. Résolution du type d'opérateur factoriel .................................................................. 384
10.2. Résolution de types pour les opérateurs de concaténation de chaînes ............................ 384
10.3. Résolution de types pour les opérateurs de valeur absolue et de négation ...................... 385
10.4. Résolution du type d'opérateur avec des inclusions de tableaux ................................... 385
10.5. Opérateur personnalisé sur un domaine .................................................................. 386
10.6. Résolution de types pour les arguments de la fonction arrondie ................................... 388
10.7. Résolution de fonction à arguments variables .......................................................... 389
10.8. Résolution de types pour les fonctions retournant un segment de chaîne ....................... 389
10.9. Conversion de types pour le stockage de character .............................................. 391
10.10. Résolution de types avec des types sous-spécifiés dans une union .............................. 392
10.11. Résolution de types dans une union simple ........................................................... 392
10.12. Résolution de types dans une union transposée ...................................................... 392
10.13. Résolution de type dans une union imbriquée ........................................................ 393
11.1. Mettre en place un index partiel pour exclure des valeurs courantes ............................. 401
11.2. Mettre en place un index partiel pour exclure les valeurs inintéressantes ....................... 402
11.3. Mettre en place un index d'unicité partiel ............................................................... 403
11.4. Ne pas utiliser les index partiels comme substitut au partitionnement ........................... 403
20.1. Exemple d'entrées de pg_hba.conf ................................................................... 647
20.2. Un exemple de fichier pg_ident.conf .............................................................. 650
34.1. Premier exemple de programme pour libpq ............................................................. 913
34.2. Deuxième exemple de programme pour libpq .......................................................... 916
34.3. Troisième exemple de programme pour libpq .......................................................... 919
35.1. Exemple de programme sur les objets larges avec libpq ............................................ 931
36.1. Programme de Démonstration SQLDA .................................................................. 987
36.2. Programme ECPG Accédant à un Large Object ..................................................... 1002
42.1. Installation manuelle de PL/Perl .......................................................................... 1249
43.1. Mettre entre guillemets des valeurs dans des requêtes dynamiques ............................. 1265
43.2. Exceptions avec UPDATE/INSERT ...................................................................... 1282
43.3. Une fonction trigger PL/pgSQL .......................................................................... 1296
43.4. Une fonction d'audit par trigger en PL/pgSQL ....................................................... 1297
43.5. Une fonction trigger en PL/pgSQL sur une vue pour un audit ................................... 1298
43.6. Une fonction trigger PL/pgSQL pour maintenir une table résumée ............................. 1299
43.7. Auditer avec les tables de transition ..................................................................... 1301
43.8. Une fonction PL/pgSQL pour un trigger d'événement .............................................. 1303
43.9. Portage d'une fonction simple de PL/SQL vers PL/pgSQL ....................................... 1311
43.10. Portage d'une fonction qui crée une autre fonction de PL/SQL vers PL/pgSQL ........... 1311
43.11. Portage d'une procédure avec manipulation de chaînes et paramètres OUT de PL/SQL
vers PL/pgSQL ........................................................................................................ 1313
43.12. Portage d'une procédure de PL/SQL vers PL/pgSQL ............................................. 1315
F.1. Créer une table distante pour les journaux applicatifs PostgreSQL au format CSV .......... 2669

xxx
Préface
Cet ouvrage représente l'adaptation française de la documentation officielle de PostgreSQL. Celle-ci a
été rédigée par les développeurs de PostgreSQL et quelques volontaires en parallèle du développement
du logiciel. Elle décrit toutes les fonctionnalités officiellement supportées par la dernière version de
PostgreSQL.

Afin de faciliter l'accès aux informations qu'il contient, cet ouvrage est organisé en plusieurs parties.
Chaque partie est destinée à une classe précise d'utilisateurs ou à des utilisateurs de niveaux d'expertise
différents :

• la Partie I est une introduction informelle destinée aux nouveaux utilisateurs ;

• la Partie II présente l'environnement du langage de requêtes SQL, notamment les types de données,
les fonctions et les optimisations utilisateurs. Tout utilisateur de PostgreSQL devrait la lire ;

• la Partie III, destinée aux administrateurs PostgreSQL, décrit l'installation et l'administration du


serveur ;

• la Partie IV décrit les interfaces de programmation ;

• la Partie V, destinée aux utilisateurs expérimentés, présente les éléments d'extension du serveur,
notamment les types de données et les fonctions utilisateurs ;

• la Partie VI contient la documentation de référence de SQL et des programmes client et serveur.


Cette partie est utilisée comme référence par les autres parties ;

• la Partie VII contient diverses informations utiles aux développeurs de PostgreSQL.

1. Définition de PostgreSQL
PostgreSQL est un système de gestion de bases de données relationnelles objet (ORDBMS) fondé sur
POSTGRES, Version 4.21. Ce dernier a été développé à l'université de Californie au département des
sciences informatiques de Berkeley. POSTGRES est à l'origine de nombreux concepts qui ne seront
rendus disponibles au sein de systèmes de gestion de bases de données commerciaux que bien plus tard.

PostgreSQL est un descendant libre du code original de Berkeley. Il supporte une grande partie du
standard SQL tout en offrant de nombreuses fonctionnalités modernes :

• requêtes complexes ;
• clés étrangères ;
• triggers ;
• vues modifiables ;
• intégrité transactionnelle ;
• contrôle des versions concurrentes (MVCC, acronyme de « MultiVersion Concurrency Control »).

De plus, PostgreSQL peut être étendu par l'utilisateur de multiples façons, en ajoutant, par exemple :

• de nouveaux types de données ;


• de nouvelles fonctions ;
• de nouveaux opérateurs ;
• de nouvelles fonctions d'agrégat ;
• de nouvelles méthodes d'indexage ;
• de nouveaux langages de procédure.

Et grâce à sa licence libérale, PostgreSQL peut être utilisé, modifié et distribué librement, quel que
soit le but visé, qu'il soit privé, commercial ou académique.
1
https://dsf.berkeley.edu/postgres.html

xxxi
Préface

2. Bref historique de PostgreSQL


Le système de bases de données relationnelles objet PostgreSQL est issu de POSTGRES, programme
écrit à l'université de Californie à Berkeley. Après des dizaines d'années de développement,
PostgreSQL annonce être devenu la base de données libre de référence.

2.1. Le projet POSTGRES à Berkeley


Le projet POSTGRES, mené par le professeur Michael Stonebraker, était sponsorisé par le DARPA
(acronyme de Defense Advanced Research Projects Agency), l'ARO (acronyme de Army Research
Office), la NSF (acronyme de National Science Foundation) et ESL, Inc. Le développement de
POSTGRES a débuté en 1986. Les concepts initiaux du système ont été présentés dans [ston86] et la
définition du modèle de données initial apparut dans [rowe87]. Le système de règles fut décrit dans
[ston87a], l'architecture du gestionnaire de stockage dans [ston87b].

Depuis, plusieurs versions majeures de POSTGRES ont vu le jour. La première « démo » devint
opérationnelle en 1987 et fut présentée en 1988 lors de la conférence ACM-SIGMOD. La version 1,
décrite dans [ston90a], fut livrée à quelques utilisateurs externes en juin 1989. Suite à la critique du
premier mécanisme de règles ([ston89]), celui-ci fut réécrit ([ston90b]) pour la version 2, présentée en
juin 1990. La version 3 apparut en 1991. Elle apporta le support de plusieurs gestionnaires de stockage,
un exécuteur de requêtes amélioré et une réécriture du gestionnaire de règles. La plupart des versions
qui suivirent, jusqu'à Postgres95 (voir plus loin), portèrent sur la portabilité et la fiabilité.

POSTGRES fut utilisé dans plusieurs applications, en recherche et en production. On peut citer, par
exemple : un système d'analyse de données financières, un programme de suivi des performances d'un
moteur à réaction, une base de données de suivi d'astéroïdes, une base de données médicale et plusieurs
systèmes d'informations géographiques. POSTGRES a aussi été utilisé comme support de formation
dans plusieurs universités. Illustra Information Technologies (devenu Informix2, maintenant détenu
par IBM3) a repris le code et l'a commercialisé. Fin 1992, POSTGRES est devenu le gestionnaire de
données principal du projet de calcul scientifique Sequoia 20004.

La taille de la communauté d'utilisateurs doubla quasiment au cours de l'année 1993. De manière


évidente, la maintenance du prototype et le support prenaient un temps considérable, temps qui aurait
dû être employé à la recherche en bases de données. Dans un souci de réduction du travail de support,
le projet POSTGRES de Berkeley se termina officiellement avec la version 4.2.

2.2. Postgres95
En 1994, Andrew Yu et Jolly Chen ajoutèrent un interpréteur de langage SQL à POSTGRES. Sous
le nouveau nom de Postgres95, le projet fut publié sur le Web comme descendant libre (OpenSource)
du code source initial de POSTGRES, version Berkeley.

Le code de Postgres95 était écrit en pur C ANSI et réduit de 25%. De nombreux changements internes
améliorèrent les performances et la maintenabilité. Les versions 1.0.x de Postgres95 passèrent le
Wisconsin Benchmark avec des performances meilleures de 30 à 50% par rapport à POSTGRES,
version 4.2. À part les correctifs de bogues, les principales améliorations furent les suivantes :

• le langage PostQUEL est remplacé par SQL (implanté sur le serveur). (La bibliothèque d'interface
libpq a été nommée à partir du langage PostQUEL.) Les requêtes imbriquées n'ont pas été supportées
avant PostgreSQL (voir plus loin), mais elles pouvaient être imitées dans Postgres95 à l'aide de
fonctions SQL utilisateur ; les agrégats furent reprogrammés, la clause GROUP BY ajoutée ;

• un nouveau programme, psql, qui utilise GNU Readline, permet l'exécution interactive de requêtes
SQL ; c'est la fin du programme monitor ;
2
https://www.ibm.com/analytics/informix
3
https://www.ibm.com/
4
http://meteora.ucsd.edu/s2k/s2k_home.html

xxxii
Préface

• une nouvelle bibliothèque cliente, libpgtcl, supporte les programmes écrits en Tcl ; un shell
exemple, pgtclsh, fournit de nouvelles commandes Tcl pour interfacer des programmes Tcl avec
Postgres95 ;

• l'interface de gestion des « Large Objects » est réécrite ; jusque-là, le seul mécanisme de stockage
de ces objets passait par le système de fichiers Inversion (« Inversion file system ») ; ce système
est abandonné ;

• le système de règles d'instance est supprimé ; les règles sont toujours disponibles en tant que règles
de réécriture ;

• un bref tutoriel présentant les possibilités du SQL ainsi que celles spécifiques à Postgres95 est
distribué avec les sources ;

• la version GNU de make est utilisée pour la construction à la place de la version BSD ; Postgres95
peut également être compilé avec un GCC sans correctif (l'alignement des doubles est corrigé).

2.3. PostgreSQL
En 1996, le nom « Postgres95 » commence à mal vieillir. Le nom choisi, PostgreSQL, souligne le lien
entre POSTGRES et les versions suivantes qui intègrent le SQL. En parallèle, la version est numérotée
6.0 pour reprendre la numérotation du projet POSTGRES de Berkeley.

Beaucoup de personnes font référence à PostgreSQL par « Postgres » (il est rare que le nom soit écrit
en capitales) par tradition ou parce que c'est plus simple à prononcer. Cet usage est accepté comme
alias ou pseudo.

Lors du développement de Postgres95, l'effort était axé sur l'identification et la compréhension des
problèmes dans le code. Avec PostgreSQL, l'accent est mis sur les nouvelles fonctionnalités, sans pour
autant abandonner les autres domaines.

L'historique de PostgreSQL à partir de ce moment est disponible dans l'Annexe E.

3. Conventions
Les conventions suivantes sont utilisées dans le synopsis d'une commande : les crochets ([ et ])
indiquent des parties optionnelles. Les accolades ({ et }) et les barres verticales (|) indiquent un choix
entre plusieurs options. Les points de suspension (...) signifient que l'élément précédent peut être
répété. Tous les autres symboles, ceci incluant les parenthèses, devraient être acceptés directement.

Lorsque cela améliore la clarté, les commandes SQL sont précédées d'une invite =>, tandis que les
commandes shell le sont par $. Dans le cadre général, les invites ne sont pas indiquées.

Un administrateur est généralement une personne en charge de l'installation et de la bonne marche du


serveur. Un utilisateur est une personne qui utilise ou veut utiliser une partie quelconque du système
PostgreSQL. Ces termes ne doivent pas être pris trop à la lettre ; cet ouvrage n'a pas d'avis figé sur
les procédures d'administration système.

4. Pour plus d'informations


En dehors de la documentation, il existe d'autres ressources concernant PostgreSQL :

Wiki

Le wiki5 de PostgreSQL contient la FAQ6 (liste des questions fréquemment posées), la liste
TODO7 et des informations détaillées sur de nombreux autres thèmes.
5
https://wiki.postgresql.org

xxxiii
Préface

Site web

Le site web8 de PostgreSQL contient des détails sur la dernière version, et bien d'autres
informations pour rendre un travail ou un investissement personnel avec PostgreSQL plus
productif.

Listes de discussion

Les listes de discussion constituent un bon endroit pour trouver des réponses à ses questions, pour
partager ses expériences avec celles d'autres utilisateurs et pour contacter les développeurs. La
consultation du site web de PostgreSQL fournit tous les détails.

Soi-même !

PostgreSQL est un projet Open Source. En tant que tel, le support dépend de la communauté des
utilisateurs. Lorsque l'on débute avec PostgreSQL, on est tributaire de l'aide des autres, soit au
travers de la documentation, soit par les listes de discussion. Il est important de faire partager à
son tour ses connaissances par la lecture des listes de discussion et les réponses aux questions.
Lorsque quelque chose est découvert qui ne figurait pas dans la documentation, pourquoi ne pas
en faire profiter les autres ? De même lors d'ajout de fonctionnalités au code.

5. Lignes de conduite pour les rapports de


bogues
Lorsque vous trouvez un bogue dans PostgreSQL, nous voulons en entendre parler. Vos rapports de
bogues jouent un rôle important pour rendre PostgreSQL plus fiable, car même avec la plus grande
attention, nous ne pouvons pas garantir que chaque partie de PostgreSQL fonctionnera sur toutes les
plates-formes et dans toutes les circonstances.

Les suggestions suivantes ont pour but de vous former à la saisie d'un rapport de bogue qui pourra
ensuite être géré de façon efficace. Il n'est pas requis de les suivre, mais ce serait à l'avantage de tous.

Nous ne pouvons pas promettre de corriger tous les bogues immédiatement. Si le bogue est évident,
critique ou affecte un grand nombre d'utilisateurs, il y a de grandes chances pour que quelqu'un s'en
charge. Il se peut que nous vous demandions d'utiliser une version plus récente pour vérifier si le
bogue est toujours présent. Ou nous pourrions décider que le bogue ne peut être corrigé avant qu'une
réécriture massive, que nous avions planifiée, ne soit faite. Ou peut-être est-ce trop difficile et que
des choses plus importantes nous attendent. Si vous avez besoin d'aide immédiatement, envisagez
l'obtention d'un contrat de support commercial.

5.1. Identifier les bogues


Avant de rapporter un bogue, merci de lire et relire la documentation pour vérifier que vous pouvez
réellement faire ce que vous essayez de faire. Si ce n'est pas clair, rapportez-le aussi ; c'est un
bogue dans la documentation. S'il s'avère que le programme fait différemment de ce qu'indique la
documentation, c'est un bogue. Ceci peut inclure les circonstances suivantes, sans s'y limiter :

• Un programme se terminant avec un signal fatal ou un message d'erreur du système d'exploitation


qui indiquerait un problème avec le programme. (Un contre-exemple pourrait être le message « disk
full », disque plein, car vous devez le régler vous-même.)

• Un programme produit une mauvaise sortie pour une entrée donnée.

• Un programme refuse d'accepter une entrée valide (c'est-à-dire telle que définie dans la
documentation).
6
https://wiki.postgresql.org/wiki/Frequently_Asked_Questions
7
https://wiki.postgresql.org/wiki/Todo
8
https://www.postgresql.org

xxxiv
Préface

• Un programme accepte une entrée invalide sans information ou message d'erreur. Mais gardez en
tête que votre idée d'entrée invalide pourrait être notre idée d'une extension ou d'une compatibilité
avec les pratiques traditionnelles.

• PostgreSQL échoue à la compilation, à la construction ou à l'installation suivant les instructions des


plates-formes supportées.

Ici, « programme » fait référence à un exécutable, pas au moteur du serveur.

Une lenteur ou une absorption des ressources n'est pas nécessairement un bogue. Lisez la
documentation ou demandez sur une des listes de discussion de l'aide concernant l'optimisation de vos
applications. Ne pas se conformer au standard SQL n'est pas nécessairement un bogue, sauf si une
telle conformité est indiquée explicitement.

Avant de continuer, vérifiez sur la liste des choses à faire ainsi que dans la FAQ pour voir si votre
bogue n'est pas déjà connu. Si vous n'arrivez pas à décoder les informations sur la liste des choses à
faire, écrivez un rapport. Le minimum que nous puissions faire est de rendre cette liste plus claire.

5.2. Que rapporter ?


Le point le plus important à se rappeler avec les rapports de bogues est de donner tous les faits et
seulement les faits. Ne spéculez pas sur ce que vous pensez qui ne va pas, sur ce qu'« il semble faire »
ou sur quelle partie le programme a une erreur. Si vous n'êtes pas familier avec l'implémentation,
vous vous tromperez probablement et vous ne nous aiderez pas. Et même si vous avez raison, des
explications complètes sont un bon supplément, mais elles ne doivent pas se substituer aux faits.
Si nous pensons corriger le bogue, nous devons toujours le reproduire nous-mêmes. Rapporter les
faits stricts est relativement simple (vous pouvez probablement copier/coller à partir de l'écran) mais,
trop souvent, des détails importants sont oubliés parce que quelqu'un a pensé qu'ils n'avaient pas
d'importance ou que le rapport serait compris.

Les éléments suivants devraient être fournis avec chaque rapport de bogue :

• La séquence exacte des étapes nécessaires pour reproduire le problème à partir du lancement du
programme. Ceci devrait se suffire ; il n'est pas suffisant d'envoyer une simple instruction SELECT
sans les commandes CREATE TABLE et INSERT qui ont précédé, si la sortie devait dépendre des
données contenues dans les tables. Nous n'avons pas le temps de comprendre le schéma de votre
base de données. Si nous sommes supposés créer nos propres données, nous allons probablement
ne pas voir le problème.

Le meilleur format pour un test suite à un problème relatif à SQL est un fichier qui peut être lancé
via l'interface psql et qui montrera le problème. (Assurez-vous de ne rien avoir dans votre fichier
de lancement ~/.psqlrc.) Un moyen facile pour créer ce fichier est d'utiliser pg_dump pour
récupérer les déclarations des tables ainsi que les données nécessaires pour mettre en place la scène.
Il ne reste plus qu'à ajouter la requête posant problème. Vous êtes encouragé à minimiser la taille
de votre exemple, mais ce n'est pas une obligation. Si le bogue est reproductible, nous le trouverons
de toute façon.

Si votre application utilise une autre interface client, telle que PHP, alors essayez d'isoler le
problème aux requêtes erronées. Nous n'allons certainement pas mettre en place un serveur web pour
reproduire votre problème. Dans tous les cas, rappelez-vous d'apporter les fichiers d'entrée exacts ;
n'essayez pas de deviner que le problème se pose pour les « gros fichiers », pour les « bases de
données de moyenne taille », etc., car cette information est trop inexacte, subjective pour être utile.

• La sortie que vous obtenez. Merci de ne pas dire que cela « ne fonctionne pas » ou s'est « arrêté
brutalement ». S'il existe un message d'erreur, montrez-le même si vous ne le comprenez pas. Si le
programme se termine avec une erreur du système d'exploitation, dites-le. Même si le résultat de
votre test est un arrêt brutal du programme ou un autre souci évident, il pourrait ne pas survenir sur
notre plate-forme. Le plus simple est de copier directement la sortie du terminal, si possible.

xxxv
Préface

Note
Si vous rapportez un message d'erreur, merci d'obtenir la forme la plus verbeuse de ce
message. Avec psql, exécutez \set VERBOSITY verbose avant tout. Si vous récupérez
le message des traces du serveur, initialisez la variable d'exécution log_error_verbosity avec
verbose pour que tous les détails soient tracés.

Note
Dans le cas d'erreurs fatales, le message d'erreur rapporté par le client pourrait ne pas
contenir toutes les informations disponibles. Jetez aussi un œil aux traces du serveur de la
base de données. Si vous ne conservez pas les traces de votre serveur, c'est le bon moment
pour commencer à le faire.

• Il est très important de préciser ce que vous attendez en sortie. Si vous écrivez uniquement « Cette
commande m'a donné cette réponse. » ou « Ce n'est pas ce que j'attendais. », nous pourrions le
lancer nous-mêmes, analyser la sortie et penser que tout est correct, car cela correspond exactement
à ce que nous attendions. Nous ne devrions pas avoir à passer du temps pour décoder la sémantique
exacte de vos commandes. Tout spécialement, ne vous contentez pas de dire que « Ce n'est pas
ce que SQL spécifie/Oracle fait. » Rechercher le comportement correct à partir de SQL n'est pas
amusant et nous ne connaissons pas le comportement de tous les autres serveurs de bases de données
relationnelles. (Si votre problème est un arrêt brutal du serveur, vous pouvez évidemment omettre
cet élément.)

• Toutes les options en ligne de commande ainsi que les autres options de lancement incluant les
variables d'environnement ou les fichiers de configuration que vous avez modifiées. Encore une
fois, soyez exact. Si vous utilisez une distribution prépackagée qui lance le serveur au démarrage,
vous devriez essayer de retrouver ce que cette distribution fait.

• Tout ce que vous avez fait de différent à partir des instructions d'installation.

• La version de PostgreSQL. Vous pouvez lancer la commande SELECT version(); pour trouver
la version du serveur sur lequel vous êtes connecté. La plupart des exécutables disposent aussi
d'une option --version ; postgres --version et psql --version devraient au moins
fonctionner. Si la fonction ou les options n'existent pas, alors votre version est bien trop ancienne
et vous devez mettre à jour. Si vous avez lancé une version préparée sous forme de paquets, tels
que les RPM, dites-le en incluant la sous-version que le paquet pourrait avoir. Si vous êtes sur une
version Git, mentionnez-le en indiquant le hachage du commit.

Si votre version est antérieure à la 11.22, nous allons certainement vous demander de mettre à jour.
Beaucoup de corrections de bogues et d'améliorations sont apportées dans chaque nouvelle version,
donc il est bien possible qu'un bogue rencontré dans une ancienne version de PostgreSQL soit
déjà corrigé. Nous ne fournissons qu'un support limité pour les sites utilisant d'anciennes versions
de PostgreSQL ; si vous avez besoin de plus de support que ce que nous fournissons, considérez
l'acquisition d'un contrat de support commercial.

• Informations sur la plate-forme. Ceci inclut le nom du noyau et sa version, bibliothèque C,


processeur, mémoires et ainsi de suite. Dans la plupart des cas, il est suffisant de préciser le vendeur
et la version, mais ne supposez pas que tout le monde sait ce que « Debian » contient ou que
tout le monde utilise des x86_64. Si vous avez des problèmes à l'installation, des informations sur
l'ensemble des outils de votre machine (compilateurs, make, etc.) sont aussi nécessaires.

N'ayez pas peur si votre rapport de bogue devient assez long. C'est un fait. Il est préférable de rapporter
tous les faits la première fois plutôt que nous ayons à vous tirer les vers du nez. D'un autre côté, si vos

xxxvi
Préface

fichiers d'entrée sont trop gros, il est préférable de demander si quelqu'un souhaite s'y plonger. Voici
un article9 qui donne quelques autres conseils sur les rapports de bogues.

Ne passez pas tout votre temps à vous demander quelles modifications apporter pour que le problème
s'en aille. Ceci ne nous aidera probablement pas à le résoudre. S'il arrive que le bogue ne puisse pas
être corrigé immédiatement, vous aurez toujours l'opportunité de chercher ceci et de partager vos
trouvailles. De même, encore une fois, ne perdez pas votre temps à deviner pourquoi le bogue existe.
Nous le trouverons assez rapidement.

Lors de la rédaction d'un rapport de bogue, merci de choisir une terminologie qui ne laisse pas place aux
confusions. Le paquet logiciel en totalité est appelé « PostgreSQL », quelquefois « Postgres » en court.
Si vous parlez spécifiquement du serveur, mentionnez-le, mais ne dites pas seulement « PostgreSQL a
planté ». Un arrêt brutal d'un seul processus serveur est assez différent de l'arrêt brutal du « postgres »
père ; merci de ne pas dire que « le serveur a planté » lorsque vous voulez dire qu'un seul processus
s'est arrêté, ni vice versa. De plus, les programmes clients tels que l'interface interactive « psql » sont
complètement séparés du moteur. Essayez d'être précis sur la provenance du problème : client ou
serveur.

5.3. Où rapporter des bogues ?


En général, envoyez vos rapports de bogue à la liste de discussion des rapports de bogue
(<[email protected]>). Nous vous demandons d'utiliser un sujet
descriptif pour votre courrier électronique, par exemple une partie du message d'erreur.

Une autre méthode consiste à remplir le formulaire web disponible sur le site web10 du projet.
Saisir un rapport de bogue de cette façon fait que celui-ci est envoyé à la liste de discussion
<[email protected]>.

Si votre rapport de bogue a des implications sur la sécurité et que vous préféreriez
qu'il ne soit pas immédiatement visible dans les archives publiques, ne l'envoyez pas
sur pgsql-bugs. Les problèmes de sécurité peuvent être rapportés de façon privée sur
<[email protected]>.

N'envoyez pas de rapports de bogue aux listes de discussion des utilisateurs, comme
<[email protected]> ou
<[email protected]>. Ces listes de discussion servent à répondre
aux questions des utilisateurs et les abonnés ne souhaitent pas recevoir de rapports de bogue. Plus
important, ils ont peu de chance de les corriger.

De même, n'envoyez pas vos rapports de bogue à la liste de discussion des développeurs
<[email protected]>. Cette liste sert aux discussions concernant le
développement de PostgreSQL et il serait bon de conserver les rapports de bogue séparément. Nous
pourrions choisir de discuter de votre rapport de bogue sur pgsql-hackers si le problème nécessite
que plus de personnes s'en occupent.

Si vous avez un problème avec la documentation, le meilleur endroit pour le rapporter est la liste de
discussion pour la documentation <[email protected]>. Soyez précis sur
la partie de la documentation qui vous déplaît.

Si votre bogue concerne un problème de portabilité sur une plate-forme non supportée, envoyez
un courrier électronique à <[email protected]>, pour que nous
puissions travailler sur le portage de PostgreSQL sur votre plate-forme.

Note
Dû, malheureusement, au grand nombre de pourriels (spam), toutes les adresses de courrier
électronique ci-dessus sont modérées sauf si vous avez souscrit. Ceci signifie qu'il y aura un
9
https://www.chiark.greenend.org.uk/~sgtatham/bugs.html
10
https://www.postgresql.org/

xxxvii
Préface

certain délai avant que l'email ne soit délivré. Si vous souhaitez souscrire aux listes, merci de
visiter https://lists.postgresql.org/ pour les instructions.

xxxviii
Partie I. Tutoriel
Bienvenue dans le tutoriel de PostgreSQL. Les chapitres suivants présentent une courte introduction à PostgreSQL,
aux concepts des bases de données relationnelles et au langage SQL à ceux qui débutent dans l'un de ces
domaines. Seules sont nécessaires des connaissances générales sur l'utilisation des ordinateurs. Aucune expérience
particulière d'Unix ou de programmation n'est requise. Ce tutoriel a surtout pour but de faire acquérir une
expérience pratique des aspects importants du système PostgreSQL. Il n'est ni exhaustif ni complet, mais
introductif.

À la suite de ce tutoriel, la lecture de la Partie II permettra d'acquérir une connaissance plus complète du langage
SQL, celle de la Partie IV des informations sur le développement d'applications. La configuration et la gestion
sont détaillées dans la Partie III.
Table des matières
1. Démarrage ................................................................................................................ 3
1.1. Installation ..................................................................................................... 3
1.2. Concepts architecturaux de base ........................................................................ 3
1.3. Création d'une base de données ......................................................................... 4
1.4. Accéder à une base ......................................................................................... 5
2. Le langage SQL ........................................................................................................ 7
2.1. Introduction .................................................................................................... 7
2.2. Concepts ........................................................................................................ 7
2.3. Créer une nouvelle table ................................................................................... 7
2.4. Remplir une table avec des lignes ...................................................................... 8
2.5. Interroger une table ......................................................................................... 9
2.6. Jointures entre les tables ................................................................................. 11
2.7. Fonctions d'agrégat ........................................................................................ 13
2.8. Mises à jour ................................................................................................. 15
2.9. Suppressions ................................................................................................. 15
3. Fonctionnalités avancées ........................................................................................... 16
3.1. Introduction .................................................................................................. 16
3.2. Vues ............................................................................................................ 16
3.3. Clés étrangères .............................................................................................. 16
3.4. Transactions ................................................................................................. 17
3.5. Fonctions de fenêtrage ................................................................................... 19
3.6. Héritage ....................................................................................................... 22
3.7. Conclusion ................................................................................................... 23

2
Chapitre 1. Démarrage
1.1. Installation
Avant de pouvoir utiliser PostgreSQL, vous devez l'installer. Il est possible que PostgreSQL soit
déjà installé dans votre environnement, soit parce qu'il est inclus dans votre distribution, soit parce
que votre administrateur système s'en est chargé. Dans ce cas, vous devriez obtenir les informations
nécessaires pour accéder à PostgreSQL dans la documentation de votre distribution ou de la part de
votre administrateur.

Si vous n'êtes pas sûr que PostgreSQL soit déjà disponible ou que vous puissiez l'utiliser pour vos
tests, vous avez la possibilité de l'installer vous-même. Le faire n'est pas difficile et peut être un bon
exercice. PostgreSQL peut être installé par n'importe quel utilisateur sans droit particulier. Aucun
accès administrateur (root) n'est requis.

Si vous installez PostgreSQL vous-même, référez-vous au Chapitre 16, pour les instructions sur
l'installation, puis revenez à ce guide quand l'installation est terminée. Nous vous conseillons de suivre
attentivement la section sur la configuration des variables d'environnement appropriées.

Si votre administrateur n'a pas fait une installation par défaut, vous pouvez avoir à effectuer un
paramétrage supplémentaire. Par exemple, si le serveur de bases de données est une machine distante,
vous aurez besoin de configurer la variable d'environnement PGHOST avec le nom du serveur de bases
de données. Il sera aussi peut-être nécessaire de configurer la variable d'environnement PGPORT. La
démarche est la suivante : si vous essayez de démarrer un programme et qu'il se plaint de ne pas
pouvoir se connecter à la base de données, vous devez consulter votre administrateur ou, si c'est vous,
la documentation pour être sûr que votre environnement est correctement paramétré. Si vous n'avez
pas compris le paragraphe précédent, lisez donc la prochaine section.

1.2. Concepts architecturaux de base


Avant de continuer, vous devez connaître les bases de l'architecture système de PostgreSQL.
Comprendre comment les parties de PostgreSQL interagissent entre elles rendra ce chapitre un peu
plus clair.

Dans le jargon des bases de données, PostgreSQL utilise un modèle client/serveur. Une session
PostgreSQL est le résultat de la coopération des processus (programmes) suivants :

• Un processus serveur, qui gère les fichiers de la base de données, accepte les connexions à la base
de la part des applications clientes et effectue sur la base les actions des clients. Le programme
serveur est appelé postgres.

• L'application cliente (l'application de l'utilisateur), qui veut effectuer des opérations sur la base de
données. Les applications clientes peuvent être de natures très différentes : un client peut être un outil
texte, une application graphique, un serveur web qui accède à la base de données pour afficher des
pages web ou un outil spécialisé dans la maintenance de bases de données. Certaines applications
clientes sont fournies avec PostgreSQL ; la plupart sont développées par les utilisateurs.

Comme souvent avec les applications client/serveur, le client et le serveur peuvent être sur des hôtes
différents. Dans ce cas, ils communiquent à travers une connexion réseau TCP/IP. Vous devez garder
cela à l'esprit, car les fichiers qui sont accessibles sur la machine cliente peuvent ne pas l'être (ou l'être
seulement en utilisant des noms de fichiers différents) sur la machine exécutant le serveur de bases
de données.

Le serveur PostgreSQL peut traiter de multiples connexions simultanées depuis les clients. Dans ce
but, il démarre un nouveau processus pour chaque connexion. À ce moment, le client et le nouveau
processus serveur communiquent sans intervention de la part du processus postgres original. Ainsi,
le processus serveur maître s'exécute toujours, attendant de nouvelles connexions clientes, tandis

3
Démarrage

que le client et les processus serveur associés vont et viennent (bien sûr, tout ceci est invisible pour
l'utilisateur ; nous le mentionnons ici seulement par exhaustivité).

1.3. Création d'une base de données


Le premier test pour voir si vous pouvez accéder au serveur de bases de données consiste à essayer
de créer une base. Un serveur PostgreSQL peut gérer plusieurs bases de données. Généralement, une
base de données distincte est utilisée pour chaque projet ou pour chaque utilisateur.

Il est possible que votre administrateur ait déjà créé une base pour vous. Dans ce cas, vous pouvez
omettre cette étape et aller directement à la prochaine section.

Pour créer une nouvelle base, nommée ma_base dans cet exemple, utilisez la commande suivante :

$ createdb ma_base

Si cette commande ne fournit aucune réponse, cette étape est réussie et vous pouvez sauter le reste
de cette section.

Si vous voyez un message similaire à :

createdb: command not found

alors PostgreSQL n'a pas été installé correctement. Soit il n'a pas été installé du tout, soit le chemin
système n'a pas été configuré pour l'inclure. Essayez d'appeler la commande avec le chemin absolu :

$ /usr/local/pgsql/bin/createdb ma_base

Le chemin sur votre serveur peut être différent. Contactez votre administrateur ou vérifiez dans les
instructions d'installation pour corriger la commande.

Voici une autre réponse possible :

createdb: could not connect to database postgres: could not connect


to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Cela signifie que le serveur n'était pas démarré, ou qu'il n'était pas démarré là où createdb l'attendait.
Une fois encore, vérifiez les instructions d'installation ou consultez votre administrateur.

Voici encore une autre réponse possible :

createdb: could not connect to database postgres: FATAL: role


"joe" does not exist

mais avec votre propre nom de connexion mentionné à la place de joe. Ceci survient si l'administrateur
n'a pas créé de compte utilisateur PostgreSQL pour vous (les comptes utilisateurs PostgreSQL sont
distincts de ceux du système d'exploitation). Si vous êtes l'administrateur, la lecture du Chapitre 21
vous expliquera comment créer de tels comptes. Vous aurez besoin de prendre l'identité de l'utilisateur
du système d'exploitation sous lequel PostgreSQL a été installé (généralement postgres) pour créer
le compte du premier utilisateur. Cela pourrait aussi signifier que vous avez un nom d'utilisateur
PostgreSQL qui est différent de celui de votre compte utilisateur du système d'exploitation. Dans ce
cas, vous avez besoin d'utiliser l'option -U ou de configurer la variable d'environnement PGUSER
pour spécifier votre nom d'utilisateur PostgreSQL.

Si vous n'avez pas les droits requis pour créer une base, vous verrez le message suivant :

createdb: database creation failed: ERROR: permission denied to


create database

4
Démarrage

Tous les utilisateurs n'ont pas l'autorisation de créer de nouvelles bases de données. Si PostgreSQL
refuse de créer des bases pour vous, alors il faut que l'administrateur vous accorde ce droit. Consultez
votre administrateur si cela arrive. Si vous avez installé vous-même l'instance PostgreSQL, alors vous
devez ouvrir une session sous le compte utilisateur que vous avez utilisé pour démarrer le serveur. 1

Vous pouvez aussi créer des bases de données avec d'autres noms. PostgreSQL vous permet de créer un
nombre quelconque de bases sur un site donné. Le nom des bases doit avoir comme premier caractère
un caractère alphabétique et est limité à 63 octets de longueur. Un choix pratique est de créer une base
avec le même nom que votre nom d'utilisateur courant. Beaucoup d'outils utilisent ce nom comme
nom par défaut pour la base : cela permet de gagner du temps en saisie. Pour créer cette base, tapez
simplement :

$ createdb

Si vous ne voulez plus utiliser votre base, vous pouvez la supprimer. Par exemple, si vous êtes
le propriétaire (créateur) de la base ma_base, vous pouvez la détruire en utilisant la commande
suivante :

$ dropdb ma_base

(Pour cette commande, le nom de la base n'est pas par défaut le nom du compte utilisateur. Vous devez
toujours en spécifier un.) Cette action supprime physiquement tous les fichiers associés avec la base
de données et elle ne peut pas être annulée, donc cela doit se faire avec beaucoup de prudence.

createdb et dropdb apportent beaucoup plus d'informations sur createdb et dropdb.

1.4. Accéder à une base


Une fois que vous avez créé la base, vous pouvez y accéder :

• Démarrez le programme en ligne de commande de PostgreSQL, appelé psql, qui vous permet de
saisir, d'éditer et d'exécuter de manière interactive des commandes SQL.
• Utilisez un outil existant avec une interface graphique comme pgAdmin ou une suite bureautique
avec un support ODBC ou JDBC pour créer et manipuler une base. Ces possibilités ne sont pas
couvertes dans ce tutoriel.
• Écrivez une application personnalisée en utilisant un des nombreux langages disponibles. Ces
possibilités sont davantage examinées dans la Partie IV.

Vous aurez probablement besoin de lancer psql pour essayer les exemples de ce tutoriel. Pour cela,
saisissez la commande suivante :

$ psql ma_base

Si vous n'indiquez pas le nom de la base, alors psql utilisera par défaut le nom de votre compte
utilisateur. Vous avez déjà découvert ce principe dans la section précédente en utilisant createdb.

Dans psql, vous serez accueilli avec le message suivant :

psql (11.22)
Type "help" for help.

ma_base=>

La dernière ligne peut aussi être :


1
Quelques explications : les noms d'utilisateurs de PostgreSQL sont différents des comptes utilisateurs du système d'exploitation. Quand vous
vous connectez à une base de données, vous pouvez choisir le nom d'utilisateur PostgreSQL que vous utilisez. Si vous ne spécifiez rien, cela
sera par défaut le même nom que votre compte système courant. En fait, il existe toujours un compte utilisateur PostgreSQL qui a le même
nom que l'utilisateur du système d'exploitation qui a démarré le serveur, et cet utilisateur a toujours le droit de créer des bases. Au lieu de vous
connecter au système en tant que cet utilisateur, vous pouvez spécifier partout l'option -U pour sélectionner un nom d'utilisateur PostgreSQL
sous lequel vous connecter.

5
Démarrage

ma_base=#

Cela veut dire que vous êtes le super-utilisateur de la base de données, ce qui est souvent le cas si vous
avez installé PostgreSQL vous-même. Être super-utilisateur ou administrateur signifie que vous n'êtes
pas sujet aux contrôles d'accès. Concernant ce tutoriel, cela n'a pas d'importance.

Si vous rencontrez des problèmes en exécutant psql, alors retournez à la section précédente. Les
diagnostics de psql et de createdb sont semblables. Si le dernier fonctionnait, alors le premier
devrait fonctionner également.

La dernière ligne affichée par psql est l'invite. Cela indique que psql est à l'écoute et que vous
pouvez saisir des requêtes SQL dans l'espace de travail maintenu par psql. Essayez ces commandes :

ma_base=> SELECT version();


version
--------------------------------------------------------------------------------
PostgreSQL 11.22 on x86_64-pc-linux-gnu, compiled by gcc (Debian
4.9.2-10) 4.9.2, 64-bit
(1 row)

ma_base=> SELECT current_date;


date
------------
2016-01-07
(1 row)

ma_base=> SELECT 2 + 2;
?column?
----------
4
(1 row)

Le programme psql dispose d'un certain nombre de commandes internes qui ne sont pas des
commandes SQL. Elles commencent avec le caractère antislash (une barre oblique inverse, « \ »). Par
exemple, vous pouvez obtenir de l'aide sur la syntaxe de nombreuses commandes SQL de PostgreSQL
en exécutant :

ma_base=> \h

Pour sortir de psql, saisissez :

ma_base=> \q

et psql se terminera et vous ramènera à votre shell. Pour plus de commandes internes, saisissez \?
à l'invite de psql. Les possibilités complètes de psql sont documentées dans psql. Dans ce tutoriel,
nous ne verrons pas ces caractéristiques explicitement, mais vous pouvez les utiliser vous-même quand
cela vous est utile.

6
Chapitre 2. Le langage SQL
2.1. Introduction
Ce chapitre fournit un panorama sur la façon d'utiliser SQL pour exécuter des opérations simples.
Ce tutoriel est seulement prévu pour vous donner une introduction et n'est, en aucun cas, un tutoriel
complet sur SQL. De nombreux livres ont été écrits sur SQL, incluant [melt93] et [date97]. Certaines
caractéristiques du langage de PostgreSQL sont des extensions de la norme.

Dans les exemples qui suivent, nous supposons que vous avez créé une base de données appelée
ma_base, comme cela a été décrit dans le chapitre précédent et que vous avez été capable de lancer
psql.

Les exemples dans ce manuel peuvent aussi être trouvés dans le répertoire src/tutorial/ de
la distribution source de PostgreSQL. (Les distributions binaires de PostgreSQL pourraient ne pas
fournir ces fichiers.) Pour utiliser ces fichiers, commencez par changer de répertoire et lancez make :

$ cd .../src/tutorial
$ make

Ceci crée les scripts et compile les fichiers C contenant des fonctions et types définis par l'utilisateur.
Puis, pour lancer le tutoriel, faites ce qui suit :

$ psql -s ma_base
...

ma_base=> \i basics.sql

La commande \i de psql lit les commandes depuis le fichier spécifié. L'option -s vous place dans
un mode pas à pas qui fait une pause avant d'envoyer chaque instruction au serveur. Les commandes
utilisées dans cette section sont dans le fichier basics.sql.

2.2. Concepts
PostgreSQL est un système de gestion de bases de données relationnelles (SGBDR). Cela signifie
que c'est un système pour gérer des données stockées dans des relations. Relation est essentiellement
un terme mathématique pour table. La notion de stockage de données dans des tables est si commune
aujourd'hui que cela peut sembler en soi évident, mais il y a de nombreuses autres manières d'organiser
des bases de données. Les fichiers et répertoires dans les systèmes d'exploitation de type Unix forment
un exemple de base de données hiérarchique. Un développement plus moderne est une base de données
orientée objet.

Chaque table est un ensemble de lignes. Chaque ligne d'une table donnée a le même ensemble de
colonnes et chaque colonne est d'un type de données particulier. Tandis que les colonnes ont un ordre
fixé dans chaque ligne, il est important de se rappeler que SQL ne garantit, d'aucune façon, l'ordre des
lignes à l'intérieur de la table (bien qu'elles puissent être explicitement triées pour l'affichage).

Les tables sont groupées dans des bases de données et un ensemble de bases gérées par une instance
unique du serveur PostgreSQL constitue une instance de bases (cluster en anglais).

2.3. Créer une nouvelle table


Vous pouvez créer une nouvelle table en spécifiant le nom de la table, suivi du nom de toutes les
colonnes et de leur type :

CREATE TABLE temps (


ville varchar(80),

7
Le langage SQL

t_basse int, -- température basse


t_haute int, -- température haute
prcp real, -- précipitation
date date
);

Vous pouvez saisir cela dans psql avec les sauts de lignes. psql reconnaîtra que la commande n'est
pas terminée jusqu'à arriver à un point-virgule.

Les espaces blancs (c'est-à-dire les espaces, les tabulations et les retours à la ligne) peuvent être
librement utilisés dans les commandes SQL. Cela signifie que vous pouvez saisir la commande ci-
dessus alignée différemment ou même sur une seule ligne. Deux tirets (« -- ») introduisent des
commentaires. Ce qui les suit est ignoré jusqu'à la fin de la ligne. SQL est insensible à la casse pour les
mots-clés et les identifiants, excepté quand les identifiants sont entre doubles guillemets pour préserver
leur casse (non fait ci-dessus).

varchar(80) spécifie un type de données pouvant contenir une chaîne de caractères arbitraires
de 80 caractères au maximum. int est le type entier normal. real est un type pour les nombres
décimaux en simple précision. date devrait s'expliquer de lui-même (oui, la colonne de type date
est aussi nommée date ; cela peut être commode ou porter à confusion, à vous de choisir).

PostgreSQL prend en charge les types SQL standards int, smallint, real, double
precision, char(N), varchar(N), date, time, timestamp et interval, ainsi que
d'autres types d'utilité générale et un riche ensemble de types géométriques. PostgreSQL peut être
personnalisé avec un nombre arbitraire de types de données définis par l'utilisateur. En conséquence,
les noms des types ne sont pas des mots-clés dans la syntaxe sauf lorsqu'il est requis de supporter des
cas particuliers dans la norme SQL.

Le second exemple stockera des villes et leur emplacement géographique associé :

CREATE TABLE villes (


nom varchar(80),
emplacement point
);

Le type point est un exemple d'un type de données spécifique à PostgreSQL.

Pour finir, vous devez savoir que si vous n'avez plus besoin d'une table ou que vous voulez la recréer
différemment, vous pouvez la supprimer en utilisant la commande suivante :

DROP TABLE nom_table;

2.4. Remplir une table avec des lignes


L'instruction INSERT est utilisée pour remplir une table avec des lignes :

INSERT INTO temps VALUES ('San Francisco', 46, 50, 0.25,


'1994-11-27');

Notez que tous les types utilisent des formats d'entrées plutôt évidents. Les constantes qui ne sont pas
des valeurs numériques simples doivent être habituellement entourées par des guillemets simples (')
comme dans l'exemple. Le type date est en réalité tout à fait flexible dans ce qu'il accepte, mais, pour
ce tutoriel, nous collerons au format non ambigu montré ici.

Le type point demande une paire de coordonnées en entrée, comme cela est montré ici :

INSERT INTO villes VALUES ('San Francisco', '(-194.0, 53.0)');

La syntaxe utilisée jusqu'à maintenant nécessite de se rappeler l'ordre des colonnes. Une syntaxe
alternative vous autorise à lister les colonnes explicitement :

8
Le langage SQL

INSERT INTO temps (ville, t_basse, t_haute, prcp, date)


VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');

Vous pouvez lister les colonnes dans un ordre différent si vous le souhaitez ou même omettre certaines
colonnes ; par exemple, si la précipitation est inconnue :

INSERT INTO temps (date, ville, t_haute, t_basse)


VALUES ('1994-11-29', 'Hayward', 54, 37);

De nombreux développeurs considèrent que le listage explicite des colonnes est un meilleur style que
de compter sur l'ordre implicite.

Merci d'exécuter toutes les commandes vues ci-dessus de façon à avoir des données sur lesquelles
travailler dans les prochaines sections.

Vous auriez pu aussi utiliser COPY pour charger de grandes quantités de données depuis des fichiers
texte. C'est habituellement plus rapide, car la commande COPY est optimisée pour cet emploi, mais
elle est moins flexible que INSERT. Par exemple :

COPY temps FROM '/home/utilisateur/temps.txt';

où le nom du fichier source doit être disponible sur la machine qui exécute le processus serveur, car
le processus serveur lit le fichier directement. Vous avez plus d'informations sur la commande COPY
dans COPY.

2.5. Interroger une table


Pour retrouver les données d'une table, elle est interrogée. Une instruction SQL SELECT est utilisée
pour faire cela. L'instruction est divisée en liste de sélection (la partie qui liste les colonnes à retourner),
une liste de tables (la partie qui liste les tables à partir desquelles les données seront retrouvées) et une
qualification optionnelle (la partie qui spécifie les restrictions). Par exemple, pour retrouver toutes les
lignes de la table temps, saisissez :

SELECT * FROM temps;

Ici, * est un raccourci pour « toutes les colonnes ». 1 Donc, le même résultat pourrait être obtenu avec :

SELECT ville, t_basse, t_haute, prcp, date FROM temps;

Le résultat devrait être ceci :

ville | t_basse | t_haute | prcp | date


---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 43 | 57 | 0 | 1994-11-29
Hayward | 37 | 54 | | 1994-11-29
(3 rows)

Vous pouvez écrire des expressions, pas seulement des références à de simples colonnes, dans la liste
de sélection. Par exemple, vous pouvez faire :

SELECT ville, (t_haute+t_basse)/2 AS temp_moy, date FROM temps;

Cela devrait donner :

ville | temp_moy | date


---------------+----------+------------
1
Alors que SELECT * est utile pour des requêtes rapides, c'est généralement considéré comme un mauvais style dans un code en production,
car l'ajout d'une colonne dans la table changerait les résultats.

9
Le langage SQL

San Francisco | 48 | 1994-11-27


San Francisco | 50 | 1994-11-29
Hayward | 45 | 1994-11-29
(3 rows)

Notez comment la clause AS est utilisée pour renommer la sortie d'une colonne (cette clause AS est
optionnelle).

Une requête peut être « qualifiée » en ajoutant une clause WHERE qui spécifie les lignes souhaitées.
La clause WHERE contient une expression booléenne et seules les lignes pour lesquelles l'expression
booléenne est vraie sont renvoyées. Les opérateurs booléens habituels (AND, OR et NOT) sont autorisés
dans la qualification. Par exemple, ce qui suit recherche le temps à San Francisco les jours pluvieux :

SELECT * FROM temps


WHERE ville = 'San Francisco' AND prcp > 0.0;

Résultat :

ville | t_basse | t_haute | prcp | date


---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
(1 row)

Vous pouvez demander à ce que les résultats d'une requête soient renvoyés dans un ordre trié :

SELECT * FROM temps


ORDER BY ville;

ville | t_basse | t_haute | prcp | date


--------------+---------+---------+------+------------
Hayward | 37 | 54 | | 1994-11-29
San Francisco | 43 | 57 | 0 | 1994-11-29
San Francisco | 46 | 50 | 0.25 | 1994-11-27

Dans cet exemple, l'ordre de tri n'est pas spécifié complètement, donc vous pouvez obtenir les lignes
San Francisco dans n'importe quel ordre. Mais, vous auriez toujours obtenu les résultats affichés ci-
dessus si vous aviez fait :

SELECT * FROM temps


ORDER BY ville, t_basse;

Vous pouvez demander que les lignes dupliquées soient supprimées du résultat d'une requête :

SELECT DISTINCT ville


FROM temps;

ville
---------------
Hayward
San Francisco
(2 rows)

De nouveau, l'ordre des lignes résultat pourrait varier. Vous pouvez vous assurer des résultats
cohérents en utilisant DISTINCT et ORDER BY ensemble : 2

SELECT DISTINCT ville


FROM temps
2
Dans certains systèmes de bases de données, ceci incluant les anciennes versions de PostgreSQL, l'implémentation de DISTINCT ordonne
automatiquement les lignes. Du coup, ORDER BY n'est pas nécessaire. Mais, ceci n'est pas requis par le standard SQL et PostgreSQL ne vous
garantit pas actuellement que DISTINCT ordonne les lignes.

10
Le langage SQL

ORDER BY ville;

2.6. Jointures entre les tables


Jusqu'ici, nos requêtes avaient seulement consulté une table à la fois. Les requêtes peuvent accéder à
plusieurs tables en même temps ou accéder à la même table de façon à ce que plusieurs lignes de la table
soient traitées en même temps. Une requête qui consulte plusieurs lignes de la même ou de différentes
tables en même temps est appelée requête de jointure. Comme exemple, supposez que vous souhaitiez
comparer la colonne ville de chaque ligne de la table temps avec la colonne nom de toutes les
lignes de la table villes et que vous choisissiez les paires de lignes où ces valeurs correspondent.

Note
Ceci est uniquement un modèle conceptuel. La jointure est habituellement exécutée d'une
manière plus efficace que la comparaison de chaque paire de lignes, mais c'est invisible pour
l'utilisateur.

Ceci sera accompli avec la requête suivante :

SELECT *
FROM temps, villes
WHERE ville = nom;

ville | t_basse | t_haute | prcp | date | nom


| emplacement
---------------+---------+---------+------+------------
+---------------+-------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San
Francisco | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | San
Francisco | (-194,53)
(2 rows)

Deux remarques à propos du résultat :

• Il n'y a pas de lignes pour la ville de Hayward dans le résultat. C'est parce qu'il n'y a aucune entrée
correspondante dans la table villes pour Hayward, donc la jointure ignore les lignes n'ayant pas
de correspondance avec la table temps. Nous verrons rapidement comment cela peut être résolu.

• Il y a deux colonnes contenant le nom des villes. C'est correct, car les listes des colonnes des tables
temps et villes sont concaténées. En pratique, ceci est indésirable, vous voudrez probablement
lister les colonnes explicitement plutôt que d'utiliser * :

SELECT ville, t_basse, t_haute, prcp, date, emplacement


FROM temps, villes
WHERE ville = nom;

Exercice : Essayez de déterminer la sémantique de cette requête quand la clause WHERE est omise.

Puisque toutes les colonnes ont un nom différent, l'analyseur a automatiquement trouvé à quelle table
elles appartiennent. Si des noms de colonnes sont communs entre les deux tables, vous aurez besoin
de qualifier les noms des colonnes pour préciser celles dont vous parlez. Par exemple :

SELECT temps.ville, temps.t_basse, temps.t_haute,


temps.prcp, temps.date, villes.emplacement
FROM temps, villes
WHERE villes.nom = temps.ville;

11
Le langage SQL

La qualification des noms de colonnes dans une requête de jointure est fréquemment considérée
comme une bonne pratique. Cela évite l'échec de la requête si un nom de colonne dupliqué est ajouté
plus tard dans une des tables.

Les requêtes de jointure vues jusqu'ici peuvent aussi être écrites sous une autre forme :

SELECT *
FROM temps INNER JOIN villes ON (temps.ville = villes.nom);

Cette syntaxe n'est pas aussi couramment utilisée que les précédentes, mais nous la montrons ici pour
vous aider à comprendre les sujets suivants.

Maintenant, nous allons essayer de comprendre comment nous pouvons avoir les entrées de Hayward.
Nous voulons que la requête parcoure la table temps et que, pour chaque ligne, elle trouve la (ou les)
ligne(s) de villes correspondante(s). Si aucune ligne correspondante n'est trouvée, nous voulons
que les valeurs des colonnes de la table villes soient remplacées par des « valeurs vides ». Ce genre
de requêtes est appelé jointure externe (outer join). (Les jointures que nous avons vues jusqu'ici sont
des jointures internes -- inner joins). La commande ressemble à cela :

SELECT *
FROM temps LEFT OUTER JOIN villes ON (temps.ville =
villes.nom);

ville | t_basse | t_haute | prcp | date | nom


| emplacement
---------------+---------+---------+------+------------
+---------------+-------------
Hayward | 37 | 54 | | 1994-11-29 |
|
San Francisco | 46 | 50 | 0.25 | 1994-11-27 | San
Francisco | (-194,53)
San Francisco | 43 | 57 | 0 | 1994-11-29 | San
Francisco | (-194,53)
(3 rows)

Cette requête est appelée une jointure externe à gauche (left outer join) parce que la table mentionnée
à la gauche de l'opérateur de jointure aura au moins une fois ses lignes dans le résultat, tandis que la
table sur la droite aura seulement les lignes qui correspondent à des lignes de la table de gauche. Lors
de l'affichage d'une ligne de la table de gauche pour laquelle il n'y a pas de correspondance dans la
table de droite, des valeurs vides (appelées NULL) sont utilisées pour les colonnes de la table de droite.

Exercice : Il existe aussi des jointures externes à droite et des jointures externes complètes. Essayez
de trouver ce qu'elles font.

Nous pouvons également joindre une table avec elle-même. Ceci est appelé une jointure réflexive.
Comme exemple, supposons que nous voulions trouver toutes les entrées de temps qui sont dans un
intervalle de températures d'autres entrées de temps. Nous avons donc besoin de comparer les colonnes
t_basse et t_haute de chaque ligne de temps aux colonnes t_basse et t_haute de toutes
les autres lignes de temps. Nous pouvons faire cela avec la requête suivante :

SELECT T1.ville, T1.t_basse AS bas, T1.t_haute AS haut,


T2.ville, T2.t_basse AS bas, T2.t_haute AS haus
FROM temps T1, temps T2
WHERE T1.t_basse < T2.t_basse
AND T1.t_haute > T2.t_haute;

ville | bas | haut | ville | bas | haut


----------------+-----+------+---------------+-----+------
San Francisco | 43 | 57 | San Francisco | 46 | 50
Hayward | 37 | 54 | San Francisco | 46 | 50

12
Le langage SQL

(2 rows)

Dans cet exemple, nous avons renommé la table temps en T1 et en T2 pour être capables de distinguer
respectivement le côté gauche et le côté droit de la jointure. Vous pouvez aussi utiliser ce genre d'alias
dans d'autres requêtes pour économiser de la frappe, c'est-à-dire :

SELECT *
FROM temps t, villes v
WHERE t.ville = v.nom;

Vous rencontrerez ce genre d'abréviation assez fréquemment.

2.7. Fonctions d'agrégat


Comme la plupart des autres produits de bases de données relationnelles, PostgreSQL supporte les
fonctions d'agrégat. Une fonction d'agrégat calcule un seul résultat à partir de plusieurs lignes en
entrée. Par exemple, il y a des agrégats pour calculer le nombre (count), la somme (sum), la moyenne
(avg), le maximum (max) et le minimum (min) d'un ensemble de lignes.

Comme exemple, nous pouvons trouver la température la plus haute parmi les températures basses
avec :

SELECT max(t_basse) FROM temps;

max
-----
46
(1 row)

Si nous voulons connaître dans quelle ville (ou villes) ces lectures se sont produites, nous pouvons
essayer :

SELECT ville FROM temps WHERE t_basse = max(t_basse);


FAUX

mais cela ne marchera pas puisque l'agrégat max ne peut pas être utilisé dans une clause WHERE (cette
restriction existe parce que la clause WHERE détermine les lignes qui seront traitées par l'agrégat ; donc
les lignes doivent être évaluées avant que les fonctions d'agrégat ne calculent leur résultat). Cependant,
comme cela est souvent le cas, la requête peut être répétée pour arriver au résultat attendu, ici en
utilisant une sous-requête :

SELECT ville FROM temps


WHERE t_basse = (SELECT max(t_basse) FROM temps);

ville
---------------
San Francisco
(1 row)

Ceci est correct, car la sous-requête est un calcul indépendant qui traite son propre agrégat séparément
à partir de ce qui se passe dans la requête externe.

Les agrégats sont également très utiles s'ils sont combinés avec les clauses GROUP BY. Par exemple,
nous pouvons obtenir le nombre de prises de température et la température la plus haute parmi les
températures basses observées dans chaque ville avec :

SELECT ville, count(*), max(t_basse)


FROM temps
GROUP BY ville;

13
Le langage SQL

ville | count | max


---------------+-------|-----
Hayward | 1 | 37
San Francisco | 2 | 46
(2 rows)

ce qui nous donne une ligne par ville dans le résultat. Chaque résultat d'agrégat est calculé avec
les lignes de la table correspondant à la ville. Nous pouvons filtrer ces lignes groupées en utilisant
HAVING :

SELECT ville, count(*), max(t_basse)


FROM temps
GROUP BY ville
HAVING max(t_basse) < 40;

ville | count | max


---------+-------+-----
Hayward | 1 | 37

ce qui nous donne le même résultat uniquement pour les villes qui ont toutes leurs valeurs de t_basse
en dessous de 40. Pour finir, si nous nous préoccupons seulement des villes dont le nom commence
par « S », nous pouvons faire :

SELECT ville, count(*), max(t_basse)


FROM temps
WHERE ville LIKE 'S%' -- 1
GROUP BY ville;

city | count | max


---------------+-------+-----
San Francisco | 2 | 46
(1 row)

1 L'opérateur LIKE fait la correspondance avec un motif ; cela est expliqué dans la Section 9.7.

Il est important de comprendre l'interaction entre les agrégats et les clauses SQL WHERE et HAVING.
La différence fondamentale entre WHERE et HAVING est que WHERE sélectionne les lignes en entrée
avant que les groupes et les agrégats ne soient traités (donc, cette clause contrôle les lignes qui se
retrouvent dans le calcul de l'agrégat), tandis que HAVING sélectionne les lignes groupées après que
les groupes et les agrégats ont été traités. Donc, la clause WHERE ne doit pas contenir de fonctions
d'agrégat ; cela n'a aucun sens d'essayer d'utiliser un agrégat pour déterminer les lignes en entrée des
agrégats. D'un autre côté, la clause HAVING contient toujours des fonctions d'agrégat (pour être précis,
vous êtes autorisés à écrire une clause HAVING qui n'utilise pas d'agrégat, mais c'est rarement utilisé.
La même condition pourra être utilisée plus efficacement par un WHERE).

Dans l'exemple précédent, nous pouvons appliquer la restriction sur le nom de la ville dans la clause
WHERE puisque cela ne nécessite aucun agrégat. C'est plus efficace que d'ajouter la restriction dans
HAVING parce que nous évitons le groupement et les calculs d'agrégat pour toutes les lignes qui ont
échoué lors du contrôle fait par WHERE.

Une autre façon de sélectionner les lignes qui vont dans le calcul d'un agrégat est d'utiliser la clause
FILTER, qui est une option par agrégat :

SELECT ville, count(*) FILTER (WHERE t_basse < 45), max(t_basse)


FROM temps
GROUP BY ville;

14
Le langage SQL

city | count | max


---------------+-------+-----
Hayward | 1 | 37
San Francisco | 1 | 46
(2 rows)

FILTER ressemble beaucoup à WHERE, sauf qu'elle supprime les lignes uniquement sur l'entrée de la
fonction d'agrégat à laquelle elle est attachée. Dans cet exemple, l'agrégat count compte seulement
les lignes pour lesquelles la colonne t_basse a une valeur inférieure à 45 alors que l'agrégat max
est toujours appliqué à toutes les lignes, donc il trouve toujours la valeur 46.

2.8. Mises à jour


Vous pouvez mettre à jour une ligne existante en utilisant la commande UPDATE. Supposez que vous
découvrez que les températures sont toutes excédentes de deux degrés après le 28 novembre. Vous
pouvez corriger les données de la façon suivante :

UPDATE temps
SET t_haute = t_haute - 2, t_basse = t_basse - 2
WHERE date > '1994-11-28';

Regardez le nouvel état des données :

SELECT * FROM temps;

ville | t_basse | t_haute | prcp | date


----------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 41 | 55 | 0 | 1994-11-29
Hayward | 35 | 52 | | 1994-11-29
(3 rows)

2.9. Suppressions
Les lignes peuvent être supprimées de la table avec la commande DELETE. Supposez que vous ne
soyez plus intéressé par le temps de Hayward. Vous pouvez faire ce qui suit pour supprimer ses lignes
de la table :

DELETE FROM temps WHERE ville = 'Hayward';

Toutes les entrées de temps pour Hayward sont supprimées.

SELECT * FROM temps;

ville | t_basse | t_haute | prcp | date


---------------+---------+---------+------+------------
San Francisco | 46 | 50 | 0.25 | 1994-11-27
San Francisco | 41 | 55 | 0 | 1994-11-29
(2 rows)

Faire très attention aux instructions de la forme

DELETE FROM nom_table;

Sans une qualification, DELETE supprimera toutes les lignes de la table donnée, la laissant vide. Le
système le fera sans demander de confirmation !

15
Chapitre 3. Fonctionnalités avancées
3.1. Introduction
Le chapitre précédent couvre les bases de l'utilisation de SQL pour le stockage et l'accès aux données
avec PostgreSQL. Il est temps d'aborder quelques fonctionnalités avancées du SQL qui simplifient
la gestion et empêchent la perte ou la corruption des données. Quelques extensions de PostgreSQL
sont également abordées.

Ce chapitre fait occasionnellement référence aux exemples disponibles dans le Chapitre 2 pour les
modifier ou les améliorer. Il est donc préférable d'avoir lu ce chapitre. Quelques exemples de ce
chapitre sont également disponibles dans advanced.sql situé dans le répertoire du tutoriel. De
plus, ce fichier contient quelques données à charger pour utiliser l'exemple. Cela n'est pas repris ici
(on peut se référer à la Section 2.1 pour savoir comment utiliser ce fichier).

3.2. Vues
Se référer aux requêtes de la Section 2.6. Si la liste des enregistrements du temps et des villes est d'un
intérêt particulier pour l'application considérée, mais qu'il devient contraignant de saisir la requête à
chaque utilisation, il est possible de créer une vue avec la requête. De ce fait, la requête est nommée
et il peut y être fait référence de la même façon qu'il est fait référence à une table :

CREATE VIEW ma_vue AS


SELECT nom, t_basse, t_haute, prcp, date, emplacement
FROM temps, villes
WHERE ville = nom;

SELECT * FROM ma_vue;

L'utilisation des vues est un aspect clé d'une bonne conception des bases de données SQL. Les
vues permettent d'encapsuler les détails de la structure des tables. Celle-ci peut alors changer avec
l'évolution de l'application, tandis que l'interface reste constante.

Les vues peuvent être utilisées dans quasiment toutes les situations où une vraie table est utilisable.
De plus, il n'est pas inhabituel de construire des vues reposant sur d'autres vues.

3.3. Clés étrangères


Soit les tables temps et villes définies dans le Chapitre 2. Il s'agit maintenant de s'assurer
que personne n'insère de ligne dans la table temps qui ne corresponde à une entrée dans la table
villes. On appelle cela maintenir l'intégrité référentielle des données. Dans les systèmes de bases de
données simplistes, lorsqu'au moins c'est possible, cela est parfois obtenu par la vérification préalable
de l'existence d'un enregistrement correspondant dans la table villes, puis par l'insertion, ou
l'interdiction, du nouvel enregistrement dans temps. Puisque cette approche, peu pratique, présente
un certain nombre d'inconvénients, PostgreSQL peut se charger du maintien de l'intégrité référentielle.

La nouvelle déclaration des tables ressemble alors à ceci :

CREATE TABLE villes (


nom varchar(80) primary key,
emplacement point
);

CREATE TABLE temps (


ville varchar(80) references villes(nom),

16
Fonctionnalités avancées

t_haute int,
t_basse int,
prcp real,
date date
);

Lors d'une tentative d'insertion d'enregistrement non valide :

INSERT INTO temps VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');

ERROR: insert or update on table "temps" violates foreign key


constraint "temps_ville_fkey"
DETAIL : Key (ville)=(a) is not present in table "villes".

Le comportement des clés étrangères peut être adapté très finement à une application particulière. Ce
tutoriel ne va pas plus loin que cet exemple simple. De plus amples informations sont accessibles dans
le Chapitre 5. Une utilisation efficace des clés étrangères améliore la qualité des applications accédant
aux bases de données. Il est donc fortement conseillé d'apprendre à les utiliser.

3.4. Transactions
Les transactions sont un concept fondamental de tous les systèmes de bases de données. Une
transaction assemble plusieurs étapes en une seule opération tout ou rien. Les états intermédiaires
entre les étapes ne sont pas visibles par les transactions concurrentes. De plus, si un échec survient qui
empêche le succès de la transaction, alors aucune des étapes n'affecte la base de données.

Si l'on considère, par exemple, la base de données d'une banque qui contient le solde de différents
comptes clients et le solde total des dépôts par branches et que l'on veut enregistrer un virement de
100 euros du compte d'Alice vers celui de Bob, les commandes SQL peuvent ressembler à cela (après
simplification) :

UPDATE comptes SET balance = balance - 100.00


WHERE nom = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE nom = (SELECT nom_branche FROM comptes WHERE nom =
'Alice');
UPDATE comptes SET balance = balance + 100.00
WHERE nom = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE nom = (SELECT nom_branche FROM comptes WHERE nom =
'Bob');

Ce ne sont pas les détails des commandes qui importent ici ; le point important est la nécessité de
plusieurs mises à jour séparées pour accomplir cette opération assez simple. Les employés de la banque
veulent être assurés que, soit toutes les commandes sont effectuées, soit aucune ne l'est. Il n'est pas
envisageable que, suite à une erreur du système, Bob reçoive 100 euros qui n'ont pas été débités du
compte d'Alice. De la même façon, Alice ne restera pas longtemps une cliente fidèle si elle est débitée
du montant sans que celui-ci ne soit crédité sur le compte de Bob. Il est important de garantir que si
quelque chose se passe mal, aucune des étapes déjà exécutées n'est prise en compte. Le regroupement
des mises à jour au sein d'une transaction apporte cette garantie. Une transaction est dite atomique :
du point de vue des autres transactions, elle passe complètement ou pas du tout.

Il est également nécessaire de garantir qu'une fois la transaction terminée et validée par la base de
données, les transactions sont enregistrées définitivement et ne peuvent être perdues, même si une
panne survient peu après. Ainsi, si un retrait d'argent est effectué par Bob, il ne faut absolument pas
que le débit de son compte disparaisse suite à une panne survenant juste après son départ de la banque.
Une base de données transactionnelle garantit que toutes les mises à jour faites lors d'une transaction
sont stockées de manière persistante (c'est-à-dire sur disque) avant que la transaction ne soit déclarée
validée.

17
Fonctionnalités avancées

Une autre propriété importante des bases de données transactionnelles est en relation étroite avec
la notion de mises à jour atomiques : quand plusieurs transactions sont lancées en parallèle, aucune
d'entre elles ne doit être capable de voir les modifications incomplètes effectuées par les autres. Ainsi,
si une transaction calcule le total de toutes les branches, inclure le débit de la branche d'Alice sans
le crédit de la branche de Bob, ou vice-versa, est une véritable erreur. Les transactions doivent donc
être tout ou rien, non seulement pour leur effet persistant sur la base de données, mais aussi pour leur
visibilité au moment de leur exécution. Les mises à jour faites jusque-là par une transaction ouverte
sont invisibles aux autres transactions jusqu'à la fin de celle-ci. À ce moment, toutes les mises à jour
deviennent simultanément visibles.

Sous PostgreSQL, une transaction est déclarée en entourant les commandes SQL de la transaction par
les commandes BEGIN et COMMIT. La transaction bancaire ressemble alors à ceci :

BEGIN;
UPDATE comptes SET balance = balance - 100.00
WHERE nom = 'Alice';
-- etc etc
COMMIT;

Si, au cours de la transaction, il est décidé de ne pas valider (peut-être la banque s'aperçoit-elle que la
balance d'Alice passe en négatif), la commande ROLLBACK peut être utilisée à la place de COMMIT.
Toutes les mises à jour réalisées jusque-là sont alors annulées.

En fait, PostgreSQL traite chaque instruction SQL comme si elle était exécutée dans une transaction.
En l'absence de commande BEGIN explicite, chaque instruction individuelle se trouve implicitement
entourée d'un BEGIN et (en cas de succès) d'un COMMIT. Un groupe d'instructions entourées par
BEGIN et COMMIT est parfois appelé bloc transactionnel.

Note
Quelques bibliothèques clientes lancent les commandes BEGIN et COMMIT automatiquement.
L'utilisateur bénéficie alors des effets des blocs transactionnels sans les demander. Vérifiez la
documentation de l'interface que vous utilisez.

Il est possible d'augmenter la granularité du contrôle des instructions au sein d'une transaction
en utilisant des points de retournement (savepoint). Ceux-ci permettent d'annuler des parties de
la transaction tout en validant le reste. Après avoir défini un point de retournement à l'aide de
SAVEPOINT, les instructions exécutées depuis ce point peuvent, au besoin, être annulées avec
ROLLBACK TO. Toutes les modifications de la base de données effectuées par la transaction entre le
moment où le point de retournement a été défini et celui où l'annulation est demandée sont annulées,
mais les modifications antérieures à ce point sont conservées.

Le retour à un point de retournement ne l'annule pas. Il reste défini et peut donc être utilisé plusieurs
fois. À l'inverse, lorsqu'il n'est plus nécessaire de revenir à un point de retournement particulier, il peut
être relâché, ce qui permet de libérer des ressources système. Il faut savoir toutefois que relâcher un
point de retournement ou y revenir relâche tous les points de retournement qui ont été définis après.

Tout ceci survient à l'intérieur du bloc de transaction, et n'est donc pas visible par les autres sessions
en cours sur la base de données. Si le bloc est validé, et à ce moment-là seulement, toutes les actions
validées deviennent immédiatement visibles par les autres sessions, tandis que les actions annulées
ne le seront jamais.

Reconsidérant la base de données de la banque, on peut supposer vouloir débiter le compte d'Alice
de $100.00, somme à créditer sur le compte de Bob, mais considérer plus tard que c'est le compte de
Wally qu'il convient de créditer. À l'aide des points de retournement, cela peut se dérouler ainsi :

BEGIN;

18
Fonctionnalités avancées

UPDATE comptes SET balance = balance - 100.00


WHERE nom = 'Alice';
SAVEPOINT mon_pointdesauvegarde;
UPDATE comptes SET balance = balance + 100.00
WHERE nom = 'Bob';
-- oups ... oublions ça et créditons le compte de Wally
ROLLBACK TO mon_pointdesauvegarde;
UPDATE comptes SET balance = balance + 100.00
WHERE nom = 'Wally';
COMMIT;

Cet exemple est bien sûr très simplifié, mais de nombreux contrôles sont réalisables au sein d'un bloc
de transaction grâce à l'utilisation des points de retournement. Qui plus est, ROLLBACK TO est le seul
moyen de regagner le contrôle d'un bloc de transaction placé dans un état d'annulation par le système
du fait d'une erreur. C'est plus rapide que de tout annuler pour tout recommencer.

3.5. Fonctions de fenêtrage


Une fonction de fenêtrage effectue un calcul sur un jeu d'enregistrements liés d'une certaine façon
à l'enregistrement courant. On peut les rapprocher des calculs réalisables par une fonction d'agrégat.
Cependant, les fonctions de fenêtrage n'entraînent pas le regroupement des enregistrements traités en
un seul, comme le ferait l'appel à une fonction d'agrégation standard. À la place, chaque enregistrement
garde son identité propre. En coulisse, la fonction de fenêtrage est capable d'accéder à d'autres
enregistrements que l'enregistrement courant du résultat de la requête.

Voici un exemple permettant de comparer le salaire d'un employé avec le salaire moyen de sa division :

SELECT nomdep, noemp, salaire, avg(salaire) OVER (PARTITION BY


nomdep) FROM salaireemp;

nomdep | noemp | salaire | avg


-----------+-------+---------+-----------------------
develop | 11 | 5200 | 5020.0000000000000000
develop | 7 | 4200 | 5020.0000000000000000
develop | 9 | 4500 | 5020.0000000000000000
develop | 8 | 6000 | 5020.0000000000000000
develop | 10 | 5200 | 5020.0000000000000000
personnel | 5 | 3500 | 3700.0000000000000000
personnel | 2 | 3900 | 3700.0000000000000000
ventes | 3 | 4800 | 4866.6666666666666667
ventes | 1 | 5000 | 4866.6666666666666667
ventes | 4 | 4800 | 4866.6666666666666667
(10 rows)

Les trois premières colonnes viennent directement de la table salaireemp, et il y a une ligne de
sortie pour chaque ligne de la table. La quatrième colonne représente une moyenne calculée sur tous
les enregistrements de la table qui ont la même valeur de nomdep que la ligne courante. (Il s'agit
effectivement de la même fonction que la fonction d'agrégat classique avg, mais la clause OVER
entraîne son exécution en tant que fonction de fenêtrage et son calcul sur la fenêtre.)

Un appel à une fonction de fenêtrage contient toujours une clause OVER qui suit immédiatement
le nom et les arguments de la fonction. C'est ce qui permet de la distinguer syntaxiquement d'une
fonction simple ou d'une fonction d'agrégat. La clause OVER détermine précisément comment les
lignes de la requête sont éclatées pour être traitées par la fonction de fenêtrage. La clause PARTITION

19
Fonctionnalités avancées

BY contenue dans OVER divise les enregistrements en groupes, ou partitions, qui partagent les
mêmes valeurs pour la (les) expression(s) contenue(s) dans la clause PARTITION BY. Pour chaque
enregistrement, la fonction de fenêtrage est calculée sur les enregistrements qui se retrouvent dans la
même partition que l'enregistrement courant.

Vous pouvez aussi contrôler l'ordre dans lequel les lignes sont traitées par les fonctions de fenêtrage en
utilisant la clause ORDER BY à l'intérieur de la clause OVER (la partition traitée par le ORDER BY n'a
de plus pas besoin de correspondre à l'ordre dans lequel les lignes seront affichées). Voici un exemple :

SELECT nomdep, noemp, salaire,


rank() OVER (PARTITION BY nomdep ORDER BY salaire DESC)
FROM salaireemp;

nomdep | noemp | salaire| rank


-----------+-------+--------+------
develop | 8 | 6000 | 1
develop | 10 | 5200 | 2
develop | 11 | 5200 | 2
develop | 9 | 4500 | 4
develop | 7 | 4200 | 5
personnel | 2 | 3900 | 1
personnel | 5 | 3500 | 2
ventes | 1 | 5000 | 1
ventes | 4 | 4800 | 2
ventes | 3 | 4800 | 2
(10 rows)

On remarque que la fonction rank produit un rang numérique pour chaque valeur ORDER BY distincte
dans la partition de la ligne courante, en utilisant l'ordre défini par la clause ORDER BY. rank n'a
pas besoin de paramètre explicite, puisque son comportement est entièrement déterminé par la clause
OVER.

Les lignes prises en compte par une fonction de fenêtrage sont celles de la table virtuelle produite par
la clause FROM de la requête filtrée par ses clauses WHERE, GROUP BY et HAVING, s'il y en a. Par
exemple, une ligne rejetée parce qu'elle ne satisfait pas à la condition WHERE n'est vue par aucune
fonction de fenêtrage. Une requête peut contenir plusieurs de ces fonctions de fenêtrage qui découpent
les données de façons différentes, par le biais de clauses OVER différentes, mais elles travaillent toutes
sur le même jeu d'enregistrements, défini par cette table virtuelle.

ORDER BY peut être omis lorsque l'ordre des enregistrements est sans importance. Il est aussi
possible d'omettre PARTITION BY, auquel cas il n'y a qu'une seule partition, contenant tous les
enregistrements.

Il y a un autre concept important associé aux fonctions de fenêtrage : pour chaque enregistrement, il
existe un jeu d'enregistrements dans sa partition appelé son window frame (cadre de fenêtre). Certaines
fonctions de fenêtrage travaillent uniquement sur les enregistrements du window frame, plutôt que
sur l'ensemble de la partition. Par défaut, si on a précisé une clause ORDER BY, la window frame
contient tous les enregistrements du début de la partition jusqu'à l'enregistrement courant, ainsi que
tous les enregistrements suivants qui sont égaux à l'enregistrement courant au sens de la clause ORDER
BY. Quand ORDER BY est omis, la window frame par défaut contient tous les enregistrements de la
partition. 1 Voici un exemple utilisant sum :

SELECT salaire, sum(salaire) OVER () FROM salaireemp;


1
Il existe des options pour définir la window frame autrement, mais ce tutoriel ne les présente pas. Voir la Section 4.2.8 pour les détails.

20
Fonctionnalités avancées

salaire| sum
--------+-------
5200 | 47100
5000 | 47100
3500 | 47100
4800 | 47100
3900 | 47100
4200 | 47100
4500 | 47100
4800 | 47100
6000 | 47100
5200 | 47100
(10 rows)

Dans l'exemple ci-dessus, puisqu'il n'y a pas d'ORDER BY dans la clause OVER, la window frame
est égale à la partition ; en d'autres termes, chaque somme est calculée sur toute la table, ce qui fait
qu'on a le même résultat pour chaque ligne du résultat. Mais si on ajoute une clause ORDER BY, on
a un résultat très différent :

SELECT salaire, sum(salaire) OVER (ORDER BY salaire) FROM


salaireemp;

salaire| sum
--------+-------
3500 | 3500
3900 | 7400
4200 | 11600
4500 | 16100
4800 | 25700
4800 | 25700
5000 | 30700
5200 | 41100
5200 | 41100
6000 | 47100
(10 rows)

Ici, sum est calculé à partir du premier salaire (c'est-à-dire le plus bas) jusqu'au salaire courant, en
incluant tous les doublons du salaire courant (remarquez les valeurs pour les salaires identiques).

Les fonctions window ne sont autorisées que dans la liste SELECT et la clause ORDER BY de la
requête. Elles sont interdites ailleurs, comme dans les clauses GROUP BY,HAVING et WHERE. La
raison en est qu'elles sont exécutées après le traitement de ces clauses. Par ailleurs, les fonctions de
fenêtrage s'exécutent après les fonctions d'agrégat classiques. Cela signifie qu'il est permis d'inclure
une fonction d'agrégat dans les arguments d'une fonction de fenêtrage, mais pas l'inverse.

S'il y a besoin de filtrer ou de grouper les enregistrements après le calcul des fonctions de fenêtrage,
une sous-requête peut être utilisée. Par exemple :

SELECT nomdep, noemp, salaire, date_embauche


FROM

21
Fonctionnalités avancées

(SELECT nomdep, noemp, salaire, date_embauche,


rank() OVER (PARTITION BY nomdep ORDER BY salaire DESC,
noemp) AS pos
FROM salaireemp
) AS ss
WHERE pos < 3;

La requête ci-dessus n'affiche que les enregistrements de la requête interne ayant un rang inférieur à 3.

Quand une requête met en jeu plusieurs fonctions de fenêtrage, il est possible d'écrire chacune avec
une clause OVER différente, mais cela entraîne des duplications de code et augmente les risques
d'erreurs si on souhaite le même comportement pour plusieurs fonctions de fenêtrage. À la place,
chaque comportement de fenêtrage peut être associé à un nom dans une clause WINDOW et ensuite
être référencé dans OVER. Par exemple :

SELECT sum(salaire) OVER w, avg(salaire) OVER w


FROM salaireemp
WINDOW w AS (PARTITION BY nomdep ORDER BY salaire DESC);

Plus de détails sur les fonctions de fenêtrage sont disponibles dans la Section 4.2.8, la Section 9.21,
la Section 7.2.5 et la page de référence SELECT.

3.6. Héritage
L'héritage est un concept issu des bases de données orientées objet. Il ouvre de nouvelles possibilités
intéressantes en conception de bases de données.

Soit deux tables : une table villes et une table capitales. Les capitales étant également des
villes, il est intéressant d'avoir la possibilité d'afficher implicitement les capitales lorsque les villes
sont listées. Un utilisateur particulièrement brillant peut écrire ceci

CREATE TABLE capitales (


nom text,
population real,
elevation int, -- (en pied)
etat char(2)
);

CREATE TABLE non_capitales (


nom text,
population real,
elevation int -- (en pied)
);

CREATE VIEW villes AS


SELECT nom, population, elevation FROM capitales
UNION
SELECT nom, population, elevation FROM non_capitales;

Cela fonctionne bien pour les requêtes, mais la mise à jour d'une même donnée sur plusieurs lignes
devient vite un horrible casse-tête.

Une meilleure solution peut être :

CREATE TABLE villes (


nom text,

22
Fonctionnalités avancées

population real,
elevation int -- (en pied)
);

CREATE TABLE capitales (


etat char(2) UNIQUE NOT NULL
) INHERITS (villes);

Dans ce cas, une ligne de capitales hérite de toutes les colonnes (nom, population et
elevation) de son parent, villes. Le type de la colonne nom est text, un type natif de
PostgreSQL pour les chaînes de caractères à longueur variable. La table capitales a une colonne
supplémentaire, etat, qui affiche l'abréviation de cet état. Sous PostgreSQL, une table peut hériter
de zéro à plusieurs autres tables.

La requête qui suit fournit un exemple d'extraction des noms de toutes les villes, en incluant les
capitales des états, situées à une elevation de plus de 500 pieds :

SELECT nom, elevation


FROM villes
WHERE elevation > 500;

ce qui renvoie :

nom | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
(3 rows)

À l'inverse, la requête qui suit récupère toutes les villes qui ne sont pas des capitales et qui sont situées
à une élévation d'au moins 500 pieds :

SELECT nom, elevation


FROM ONLY villes
WHERE elevation > 500;

nom | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953
(2 rows)

Ici, ONLY avant villes indique que la requête ne doit être exécutée que sur la table villes, et non
pas sur les tables en dessous de villes dans la hiérarchie des héritages. La plupart des commandes
déjà évoquées -- SELECT, UPDATE et DELETE -- supportent cette notation (ONLY).

Note
Bien que l'héritage soit fréquemment utile, il n'a pas été intégré avec les contraintes d'unicité
et les clés étrangères, ce qui limite son utilité. Voir la Section 5.9 pour plus de détails.

3.7. Conclusion
PostgreSQL dispose d'autres fonctionnalités non décrites dans ce tutoriel d'introduction orienté vers
les nouveaux utilisateurs de SQL. Ces fonctionnalités sont discutées plus en détail dans le reste de
ce livre.

23
Fonctionnalités avancées

Si une introduction plus approfondie est nécessaire, le lecteur peut visiter le site web2 de PostgreSQL
qui fournit des liens vers d'autres ressources.

2
https://www.postgresql.org

24
Partie II. Langage SQL
Cette partie présente l'utilisation du langage SQL au sein de PostgreSQL. La syntaxe générale de SQL y
est expliquée, ainsi que la création des structures de stockage des données, le peuplement de la base et son
interrogation. La partie centrale liste les types de données et les fonctions disponibles ainsi que leur utilisation dans
les requêtes SQL. Le reste traite de l'optimisation de la base de données en vue d'obtenir des performances idéales.

L'information dans cette partie est présentée pour qu'un utilisateur novice puisse la suivre du début à la fin et
obtenir ainsi une compréhension complète des sujets sans avoir à effectuer de fréquents sauts entre les chapitres.
Les chapitres sont indépendants. Un utilisateur plus expérimenté pourra, donc, ne consulter que les chapitres
l'intéressant. L'information est présentée dans un style narratif par unité thématique. Les lecteurs qui cherchent
une description complète d'une commande particulière peuvent se référer à la Partie VI.

Pour profiter pleinement de cette partie, il est nécessaire de savoir se connecter à une base PostgreSQL et d'y
exécuter des commandes SQL. Les lecteurs qui ne sont pas familiers avec ces prérequis sont encouragés à lire
préalablement la Partie I.

Les commandes SQL sont généralement saisies à partir du terminal interactif de PostgreSQL, psql. D'autres
programmes possédant des fonctionnalités similaires peuvent également être utilisés.
Table des matières
4. Syntaxe SQL ........................................................................................................... 33
4.1. Structure lexicale ........................................................................................... 33
4.1.1. identificateurs et mots-clés .................................................................... 33
4.1.2. Constantes ......................................................................................... 35
4.1.3. Opérateurs ......................................................................................... 40
4.1.4. Caractères spéciaux ............................................................................. 40
4.1.5. Commentaires .................................................................................... 41
4.1.6. Précédence d'opérateurs ........................................................................ 41
4.2. Expressions de valeurs ................................................................................... 42
4.2.1. Références de colonnes ........................................................................ 43
4.2.2. Paramètres de position ......................................................................... 43
4.2.3. Indices .............................................................................................. 44
4.2.4. Sélection de champs ............................................................................ 44
4.2.5. Appels d'opérateurs ............................................................................. 45
4.2.6. Appels de fonctions ............................................................................. 45
4.2.7. Expressions d'agrégat ........................................................................... 46
4.2.8. Appels de fonction de fenêtrage ............................................................. 48
4.2.9. Conversions de type ............................................................................ 51
4.2.10. Expressions de collationnement ............................................................ 51
4.2.11. Sous-requêtes scalaires ....................................................................... 52
4.2.12. Constructeurs de tableaux ................................................................... 52
4.2.13. Constructeurs de lignes ...................................................................... 54
4.2.14. Règles d'évaluation des expressions ...................................................... 55
4.3. Fonctions appelantes ...................................................................................... 57
4.3.1. En utilisant la notation par position ........................................................ 57
4.3.2. En utilisant la notation par nom ............................................................. 58
4.3.3. En utilisant la notation mixée ................................................................ 59
5. Définition des données .............................................................................................. 60
5.1. Notions fondamentales sur les tables ................................................................. 60
5.2. Valeurs par défaut ......................................................................................... 61
5.3. Contraintes ................................................................................................... 62
5.3.1. Contraintes de vérification .................................................................... 62
5.3.2. Contraintes de non-nullité (NOT NULL) ................................................. 64
5.3.3. Contraintes d'unicité ............................................................................ 65
5.3.4. Clés primaires .................................................................................... 66
5.3.5. Clés étrangères ................................................................................... 67
5.3.6. Contraintes d'exclusion ........................................................................ 70
5.4. Colonnes système .......................................................................................... 70
5.5. Modification des tables ................................................................................... 71
5.5.1. Ajouter une colonne ............................................................................ 72
5.5.2. Supprimer une colonne ........................................................................ 72
5.5.3. Ajouter une contrainte ......................................................................... 73
5.5.4. Supprimer une contrainte ...................................................................... 73
5.5.5. Modifier la valeur par défaut d'une colonne ............................................. 73
5.5.6. Modifier le type de données d'une colonne .............................................. 74
5.5.7. Renommer une colonne ........................................................................ 74
5.5.8. Renommer une table ............................................................................ 74
5.6. Droits .......................................................................................................... 74
5.7. Politiques de sécurité niveau ligne .................................................................... 75
5.8. Schémas ....................................................................................................... 82
5.8.1. Créer un schéma ................................................................................. 82
5.8.2. Le schéma public ................................................................................ 83
5.8.3. Chemin de parcours des schémas ........................................................... 83
5.8.4. Schémas et privilèges .......................................................................... 85
5.8.5. Le schéma du catalogue système ........................................................... 85

26
Langage SQL

5.8.6. Utilisation .......................................................................................... 85


5.8.7. Portabilité .......................................................................................... 86
5.9. L'héritage ..................................................................................................... 86
5.9.1. Restrictions ........................................................................................ 89
5.10. Partitionnement de tables .............................................................................. 90
5.10.1. Aperçu ............................................................................................ 90
5.10.2. Partitionnement déclaratif ................................................................... 91
5.10.3. Partitionnement utilisant l'héritage ........................................................ 96
5.10.4. Élagage de partition ......................................................................... 101
5.10.5. Partitionnement et Contrainte d'exclusion ............................................. 103
5.10.6. Bonnes pratiques pour le partitionnement déclaratif ................................ 104
5.11. Données distantes ....................................................................................... 104
5.12. Autres objets de la base de données .............................................................. 105
5.13. Gestion des dépendances ............................................................................. 105
6. Manipulation de données ......................................................................................... 108
6.1. Insérer des données ...................................................................................... 108
6.2. Actualiser les données .................................................................................. 109
6.3. Supprimer des données ................................................................................. 110
6.4. Renvoyer des données provenant de lignes modifiées ......................................... 110
7. Requêtes ............................................................................................................... 112
7.1. Aperçu ....................................................................................................... 112
7.2. Expressions de table ..................................................................................... 112
7.2.1. Clause FROM .................................................................................... 113
7.2.2. Clause WHERE .................................................................................. 122
7.2.3. Clauses GROUP BY et HAVING .......................................................... 123
7.2.4. GROUPING SETS, CUBE et ROLLUP .................................................. 125
7.2.5. Traitement de fonctions Window .......................................................... 127
7.3. Listes de sélection ........................................................................................ 128
7.3.1. Éléments de la liste de sélection ........................................................... 128
7.3.2. Labels de colonnes ............................................................................ 128
7.3.3. DISTINCT ...................................................................................... 129
7.4. Combiner des requêtes .................................................................................. 130
7.5. Tri des lignes .............................................................................................. 131
7.6. LIMIT et OFFSET ...................................................................................... 132
7.7. Listes VALUES ............................................................................................ 132
7.8. Requêtes WITH (Common Table Expressions) ................................................... 133
7.8.1. SELECT dans WITH .......................................................................... 134
7.8.2. Ordres de Modification de Données avec WITH ...................................... 137
8. Types de données ................................................................................................... 140
8.1. Types numériques ........................................................................................ 141
8.1.1. Types entiers .................................................................................... 142
8.1.2. Nombres à précision arbitraire ............................................................. 142
8.1.3. Types à virgule flottante ..................................................................... 144
8.1.4. Types seriés ..................................................................................... 145
8.2. Types monétaires ......................................................................................... 146
8.3. Types caractère ........................................................................................... 147
8.4. Types de données binaires ............................................................................. 149
8.4.1. Le format hexadécimal bytea ............................................................ 150
8.4.2. Le format d'échappement bytea ......................................................... 150
8.5. Types date/heure .......................................................................................... 152
8.5.1. Saisie des dates et heures .................................................................... 153
8.5.2. Affichage des dates et heures .............................................................. 157
8.5.3. Fuseaux horaires ............................................................................... 158
8.5.4. Saisie d'intervalle .............................................................................. 159
8.5.5. Affichage d'intervalles ........................................................................ 161
8.6. Type booléen .............................................................................................. 162
8.7. Types énumération ....................................................................................... 163
8.7.1. Déclaration de types énumérés ............................................................. 163

27
Langage SQL

8.7.2. Tri .................................................................................................. 164


8.7.3. Sûreté du type .................................................................................. 164
8.7.4. Détails d'implémentation ..................................................................... 165
8.8. Types géométriques ...................................................................................... 165
8.8.1. Points .............................................................................................. 166
8.8.2. Lines ............................................................................................... 166
8.8.3. Segments de droite ............................................................................ 166
8.8.4. Boîtes .............................................................................................. 166
8.8.5. Chemins .......................................................................................... 167
8.8.6. Polygones ........................................................................................ 167
8.8.7. Cercles ............................................................................................ 167
8.9. Types adresses réseau ................................................................................... 167
8.9.1. inet .............................................................................................. 168
8.9.2. cidr .............................................................................................. 168
8.9.3. inet vs cidr ................................................................................. 169
8.9.4. macaddr ........................................................................................ 169
8.9.5. macaddr8 ...................................................................................... 169
8.10. Type chaîne de bits .................................................................................... 170
8.11. Types de recherche plein texte ..................................................................... 171
8.11.1. tsvector ..................................................................................... 171
8.11.2. tsquery ....................................................................................... 173
8.12. Type UUID ............................................................................................... 174
8.13. Type XML ................................................................................................ 175
8.13.1. Créer des valeurs XML .................................................................... 175
8.13.2. Gestion de l'encodage ....................................................................... 176
8.13.3. Accéder aux valeurs XML ................................................................ 176
8.14. Types JSON .............................................................................................. 177
8.14.1. Syntaxe d'entrée et de sortie JSON ..................................................... 178
8.14.2. Concevoir des documents JSON efficacement ....................................... 179
8.14.3. Existence et inclusion jsonb ............................................................ 180
8.14.4. Indexation jsonb ........................................................................... 182
8.14.5. Transformations ............................................................................... 185
8.15. Tableaux ................................................................................................... 185
8.15.1. Déclaration des types tableaux ........................................................... 185
8.15.2. Saisie de valeurs de type tableau ........................................................ 186
8.15.3. Accès aux tableaux .......................................................................... 187
8.15.4. Modification de tableaux ................................................................... 189
8.15.5. Recherche dans les tableaux .............................................................. 192
8.15.6. Syntaxe d'entrée et de sortie des tableaux ............................................. 193
8.16. Types composites ....................................................................................... 194
8.16.1. Déclaration de types composites ......................................................... 194
8.16.2. Construire des valeurs composites ...................................................... 195
8.16.3. Accéder aux types composites ............................................................ 196
8.16.4. Modifier les types composites ............................................................ 197
8.16.5. Utiliser des types composites dans les requêtes ...................................... 197
8.16.6. Syntaxe en entrée et sortie d'un type composite ..................................... 200
8.17. Types intervalle de valeurs .......................................................................... 201
8.17.1. Types internes d'intervalle de valeurs .................................................. 201
8.17.2. Exemples ....................................................................................... 201
8.17.3. Bornes inclusives et exclusives .......................................................... 202
8.17.4. Intervalles de valeurs infinis (sans borne) ............................................. 202
8.17.5. Saisie/affichage d'intervalle de valeurs ................................................. 202
8.17.6. Construire des intervalles de valeurs ................................................... 203
8.17.7. Types intervalle de valeurs discrètes ................................................... 204
8.17.8. Définir de nouveaux types intervalle de valeurs ..................................... 204
8.17.9. Indexation ...................................................................................... 205
8.17.10. Contraintes sur les intervalles de valeurs ............................................ 206
8.18. Types domaine .......................................................................................... 207

28
Langage SQL

8.19. Types identifiant d'objet .............................................................................. 207


8.20. pg_lsn Type .............................................................................................. 209
8.21. Pseudo-Types ............................................................................................ 209
9. Fonctions et opérateurs ............................................................................................ 211
9.1. Opérateurs logiques ...................................................................................... 211
9.2. Fonctions et opérateurs de comparaison ........................................................... 211
9.3. Fonctions et opérateurs mathématiques ............................................................ 214
9.4. Fonctions et opérateurs de chaînes .................................................................. 218
9.4.1. format .......................................................................................... 230
9.5. Fonctions et opérateurs de chaînes binaires ....................................................... 232
9.6. Fonctions et opérateurs sur les chaînes de bits ................................................... 234
9.7. Correspondance de motif ............................................................................... 235
9.7.1. LIKE .............................................................................................. 236
9.7.2. Expressions rationnelles SIMILAR TO ................................................ 237
9.7.3. Expressions rationnelles POSIX ........................................................... 238
9.8. Fonctions de formatage des types de données ................................................... 251
9.9. Fonctions et opérateurs sur date/heure ............................................................. 258
9.9.1. EXTRACT, date_part .................................................................... 264
9.9.2. date_trunc .................................................................................. 268
9.9.3. AT TIME ZONE ............................................................................. 269
9.9.4. Date/Heure courante .......................................................................... 270
9.9.5. Retarder l'exécution ........................................................................... 271
9.10. Fonctions de support enum .......................................................................... 272
9.11. Fonctions et opérateurs géométriques ............................................................. 273
9.12. Fonctions et opérateurs sur les adresses réseau ................................................ 277
9.13. Fonctions et opérateurs de la recherche plein texte ........................................... 279
9.14. Fonctions XML ......................................................................................... 286
9.14.1. Produire un contenu XML ................................................................. 286
9.14.2. Prédicats XML ................................................................................ 291
9.14.3. Traiter du XML ............................................................................... 292
9.14.4. Transformer les tables en XML .......................................................... 297
9.15. Fonctions et opérateurs JSON ....................................................................... 300
9.16. Fonctions de manipulation de séquences ......................................................... 310
9.17. Expressions conditionnelles .......................................................................... 313
9.17.1. CASE ............................................................................................. 313
9.17.2. COALESCE ..................................................................................... 314
9.17.3. NULLIF ......................................................................................... 315
9.17.4. GREATEST et LEAST ...................................................................... 315
9.18. Fonctions et opérateurs de tableaux ............................................................... 315
9.19. Fonctions et opérateurs sur les données de type range ....................................... 319
9.20. Fonctions d'agrégat ..................................................................................... 321
9.21. Fonctions Window ..................................................................................... 329
9.22. Expressions de sous-requêtes ........................................................................ 331
9.22.1. EXISTS ......................................................................................... 331
9.22.2. IN ................................................................................................. 332
9.22.3. NOT IN ........................................................................................ 332
9.22.4. ANY/SOME ...................................................................................... 333
9.22.5. ALL ............................................................................................... 333
9.22.6. Comparaison de lignes seules ............................................................ 334
9.23. Comparaisons de lignes et de tableaux ........................................................... 334
9.23.1. IN ................................................................................................. 334
9.23.2. NOT IN ........................................................................................ 334
9.23.3. ANY/SOME (array) ............................................................................ 335
9.23.4. ALL (array) .................................................................................... 335
9.23.5. Comparaison de constructeur de lignes ................................................ 335
9.23.6. Comparaison de type composite ......................................................... 336
9.24. Fonctions retournant des ensembles ............................................................... 337
9.25. Fonctions d'informations système .................................................................. 340

29
Langage SQL

9.26. Fonctions d'administration système ................................................................ 358


9.26.1. Fonctions pour le paramétrage ........................................................... 358
9.26.2. Fonctions d'envoi de signal du serveur ................................................. 358
9.26.3. Fonctions de contrôle de la sauvegarde ................................................ 359
9.26.4. Fonctions de contrôle de la restauration ............................................... 362
9.26.5. Fonctions de synchronisation des images de base ................................... 364
9.26.6. Fonctions de réplication .................................................................... 364
9.26.7. Fonctions de gestion des objets du serveur ........................................... 369
9.26.8. Fonctions de maintenance des index .................................................... 372
9.26.9. Fonctions génériques d'accès aux fichiers ............................................. 373
9.26.10. Fonctions pour les verrous consultatifs ............................................... 374
9.27. Fonctions trigger ........................................................................................ 377
9.28. Fonctions des triggers sur les événements ....................................................... 377
9.28.1. Récupérer les modifications à la fin de la commande .............................. 377
9.28.2. Traitement des objets supprimés par une commande DDL ....................... 378
9.28.3. Gérer un événement de modification de table ........................................ 380
10. Conversion de types .............................................................................................. 381
10.1. Aperçu ..................................................................................................... 381
10.2. Opérateurs ................................................................................................ 382
10.3. Fonctions .................................................................................................. 386
10.4. Stockage de valeurs .................................................................................... 390
10.5. Constructions UNION, CASE et constructions relatives ...................................... 391
10.6. Colonnes de sortie du SELECT .................................................................... 393
11. Index .................................................................................................................. 394
11.1. Introduction ............................................................................................... 394
11.2. Types d'index ............................................................................................ 395
11.3. Index multicolonnes .................................................................................... 397
11.4. Index et ORDER BY .................................................................................. 398
11.5. Combiner des index multiples ...................................................................... 399
11.6. Index d'unicité ........................................................................................... 399
11.7. Index d'expressions ..................................................................................... 400
11.8. Index partiels ............................................................................................. 401
11.9. Parcours d'index seul et index couvrants ......................................................... 404
11.10. Classes et familles d'opérateurs ................................................................... 407
11.11. Index et collationnements ........................................................................... 408
11.12. Examiner l'utilisation des index ................................................................... 409
12. Recherche plein texte ............................................................................................ 411
12.1. Introduction ............................................................................................... 411
12.1.1. Qu'est-ce qu'un document ? ............................................................... 412
12.1.2. Correspondance de base d'un texte ...................................................... 413
12.1.3. Configurations ................................................................................. 415
12.2. Tables et index .......................................................................................... 415
12.2.1. Rechercher dans une table ................................................................. 415
12.2.2. Créer des index ............................................................................... 416
12.3. Contrôler la recherche plein texte .................................................................. 418
12.3.1. Analyser des documents .................................................................... 418
12.3.2. Analyser des requêtes ....................................................................... 419
12.3.3. Ajouter un score aux résultats d'une recherche ...................................... 422
12.3.4. Surligner les résultats ....................................................................... 424
12.4. Fonctionnalités supplémentaires .................................................................... 425
12.4.1. Manipuler des documents .................................................................. 426
12.4.2. Manipuler des requêtes ..................................................................... 426
12.4.3. Triggers pour les mises à jour automatiques .......................................... 429
12.4.4. Récupérer des statistiques sur les documents ......................................... 431
12.5. Analyseurs ................................................................................................ 431
12.6. Dictionnaires ............................................................................................. 433
12.6.1. Termes courants .............................................................................. 434
12.6.2. Dictionnaire simple .......................................................................... 435

30
Langage SQL

12.6.3. Dictionnaire des synonymes .............................................................. 436


12.6.4. Dictionnaire thésaurus ...................................................................... 438
12.6.5. Dictionnaire Ispell ........................................................................... 440
12.6.6. Dictionnaire Snowball ...................................................................... 443
12.7. Exemple de configuration ............................................................................ 443
12.8. Tester et déboguer la recherche plein texte ..................................................... 445
12.8.1. Test d'une configuration .................................................................... 445
12.8.2. Test de l'analyseur ........................................................................... 448
12.8.3. Test des dictionnaires ....................................................................... 449
12.9. Types d'index préférées pour la recherche plein texte ........................................ 450
12.10. Support de psql ........................................................................................ 451
12.11. Limites ................................................................................................... 454
13. Contrôle d'accès simultané ..................................................................................... 455
13.1. Introduction ............................................................................................... 455
13.2. Isolation des transactions ............................................................................. 455
13.2.1. Niveau d'isolation Read committed (lecture uniquement des données
validées) ................................................................................................... 456
13.2.2. Repeatable Read Isolation Level ......................................................... 458
13.2.3. Niveau d'Isolation Serializable ........................................................... 459
13.3. Verrouillage explicite .................................................................................. 462
13.3.1. Verrous de niveau table .................................................................... 462
13.3.2. Verrous au niveau ligne .................................................................... 464
13.3.3. Verrous au niveau page .................................................................... 465
13.3.4. Verrous morts (blocage) .................................................................... 465
13.3.5. Verrous informatifs .......................................................................... 466
13.4. Vérification de cohérence des données au niveau de l'application ........................ 467
13.4.1. Garantir la Cohérence avec des Transactions Serializable ........................ 468
13.4.2. Garantir la Cohérence avec des Verrous Bloquants Explicites ................... 468
13.5. Avertissements ........................................................................................... 469
13.6. Verrous et index ........................................................................................ 469
14. Conseils sur les performances ................................................................................. 471
14.1. Utiliser EXPLAIN ...................................................................................... 471
14.1.1. EXPLAIN Basics ............................................................................. 471
14.1.2. EXPLAIN ANALYZE ...................................................................... 478
14.1.3. Avertissements ................................................................................ 482
14.2. Statistiques utilisées par le planificateur ......................................................... 483
14.2.1. Statistiques monocolonne .................................................................. 483
14.2.2. Statistiques étendues ........................................................................ 485
14.3. Contrôler le planificateur avec des clauses JOIN explicites ................................ 487
14.4. Remplir une base de données ....................................................................... 489
14.4.1. Désactivez la validation automatique (autocommit) ................................ 489
14.4.2. Utilisez COPY ................................................................................. 490
14.4.3. Supprimez les index ......................................................................... 490
14.4.4. Suppression des contraintes de clés étrangères ...................................... 490
14.4.5. Augmentez maintenance_work_mem ............................................ 491
14.4.6. Augmenter max_wal_size ............................................................. 491
14.4.7. Désactiver l'archivage des journaux de transactions et la réplication en
flux .......................................................................................................... 491
14.4.8. Lancez ANALYZE après .................................................................... 491
14.4.9. Quelques notes sur pg_dump ............................................................. 492
14.5. Configuration avec une perte acceptée ........................................................... 492
15. Requêtes parallélisées ............................................................................................ 494
15.1. Comment fonctionne la parallélisation des requêtes .......................................... 494
15.2. Quand la parallélisation des requêtes peut-elle être utilisée ? .............................. 495
15.3. Plans parallélisés ........................................................................................ 496
15.3.1. Parcours parallélisés ......................................................................... 496
15.3.2. Jointures parallélisées ....................................................................... 497
15.3.3. Agrégations parallélisées ................................................................... 497

31
Langage SQL

15.3.4. Parallel Append ............................................................................... 497


15.3.5. Conseils pour les plans parallélisés ..................................................... 498
15.4. Sécurité de la parallélisation ......................................................................... 498
15.4.1. Marquage de parallélisation pour les fonctions et agrégats ....................... 499

32
Chapitre 4. Syntaxe SQL
Ce chapitre décrit la syntaxe de SQL. Il donne les fondements pour comprendre les chapitres suivants
qui iront plus en détail sur la façon dont les commandes SQL sont appliquées pour définir et modifier
des données.

Nous avertissons aussi nos utilisateurs, déjà familiers avec le SQL, qu'ils doivent lire ce chapitre très
attentivement, car il existe plusieurs règles et concepts implémentés différemment suivant les bases
de données SQL ou spécifiques à PostgreSQL.

4.1. Structure lexicale


Une entrée SQL consiste en une séquence de commandes. Une commande est composée d'une
séquence de jetons, terminés par un point-virgule (« ; »). La fin du flux en entrée termine aussi une
commande. Les jetons valides dépendent de la syntaxe particulière de la commande.

Un jeton peut être un mot-clé, un identificateur, un identificateur entre guillemets, une constante ou
un symbole de caractère spécial. Les jetons sont normalement séparés par des espaces blancs (espace,
tabulation, nouvelle ligne), mais n'ont pas besoin de l'être s'il n'y a pas d'ambiguïté (ce qui est seulement
le cas si un caractère spécial est adjacent à des jetons d'autres types).

Par exemple, ce qui suit est (syntaxiquement) valide pour une entrée SQL :

SELECT * FROM MA_TABLE;


UPDATE MA_TABLE SET A = 5;
INSERT INTO MA_TABLE VALUES (3, 'salut ici');

C'est une séquence de trois commandes, une par ligne (bien que cela ne soit pas requis , plusieurs
commandes peuvent se trouver sur une même ligne et une commande peut se répartir sur plusieurs
lignes).

De plus, des commentaires peuvent se trouver dans l'entrée SQL. Ce ne sont pas des jetons, ils sont
réellement équivalents à un espace blanc.

La syntaxe SQL n'est pas très cohérente en ce qui concerne les jetons identificateurs des commandes,
lesquels sont des opérandes ou des paramètres. Les premiers jetons sont généralement le nom de la
commande. Dans l'exemple ci-dessus, nous parlons d'une commande « SELECT », d'une commande
« UPDATE » et d'une commande « INSERT ». Mais en fait, la commande UPDATE requiert toujours
un jeton SET apparaissant à une certaine position, et cette variante particulière d'INSERT requiert
aussi un VALUES pour être complète. Les règles précises de syntaxe pour chaque commande sont
décrites dans la Partie VI.

4.1.1. identificateurs et mots-clés


Les jetons tels que SELECT, UPDATE ou VALUES dans l'exemple ci-dessus sont des exemples de
mots-clés, c'est-à-dire des mots qui ont une signification dans le langage SQL. Les jetons MA_TABLE
et A sont des exemples d'identificateurs. Ils identifient des noms de tables, colonnes ou d'autres
objets de la base de données, suivant la commande qui a été utilisée. Du coup, ils sont quelques fois
simplement nommés des « noms ». Les mots-clés et les identificateurs ont la même structure lexicale,
signifiant que quelqu'un ne peut pas savoir si un jeton est un identificateur ou un mot-clé sans connaître
le langage. Une liste complète des mots-clés est disponible dans l'Annexe C.

Les identificateurs et les mots-clés SQL doivent commencer avec une lettre (a-z, mais aussi des lettres
de marques diacritiques différentes et des lettres non latines) ou un tiret bas (_). Les caractères suivants
dans un identificateur ou dans un mot-clé peuvent être des lettres, des tirets bas, des chiffres (0-9) ou
des signes dollar ($). Notez que les signes dollar ne sont pas autorisés en tant qu'identificateur d'après
le standard SQL, donc leur utilisation pourrait rendre les applications moins portables. Le standard
SQL ne définira pas un mot-clé contenant des chiffres ou commençant ou finissant par un tiret bas,

33
Syntaxe SQL

donc les identificateurs de cette forme sont sûrs de ne pas entrer en conflit avec les futures extensions
du standard.

Le système utilise au plus NAMEDATALEN-1 octets d'un identificateur ; les noms longs peuvent être
écrits dans des commandes, mais ils seront tronqués. Par défaut, NAMEDATALEN vaut 64. Du coup,
la taille maximale de l'identificateur est de 63 octets. Si cette limite est problématique, elle peut être
élevée en modifiant NAMEDATALEN dans src/include/pg_config_manual.h.

Les mots-clés et les identificateurs sans guillemets doubles sont insensibles à la casse. Du coup :

UPDATE MA_TABLE SET A = 5;

peut aussi s'écrire de cette façon :

uPDaTE ma_TabLE SeT a = 5;

Une convention couramment utilisée revient à écrire les mots-clés en majuscule et les noms en
minuscule, c'est-à-dire :

UPDATE ma_table SET a = 5;

Voici un deuxième type d'identificateur : l'identificateur délimité ou l'identificateur entre guillemets.


Il est formé en englobant une séquence arbitraire de caractères entre des guillemets doubles ("). Un
identificateur délimité est toujours un identificateur, jamais un mot-clé. Donc, "select" pourrait
être utilisé pour faire référence à une colonne ou à une table nommée « select », alors qu'un select
sans guillemets sera pris pour un mot-clé et, du coup, pourrait provoquer une erreur d'analyse lorsqu'il
est utilisé alors qu'un nom de table ou de colonne est attendu. L'exemple peut être écrit avec des
identificateurs entre guillemets comme ceci :

UPDATE "ma_table" SET "a" = 5;

Les identificateurs entre guillemets peuvent contenir tout caractère autre que celui de code 0. (Pour
inclure un guillemet double, écrivez deux guillemets doubles.) Ceci permet la construction de noms de
tables et de colonnes qui ne seraient pas possibles autrement, comme des noms contenant des espaces
ou des arobases. La limitation de la longueur s'applique toujours.

Une variante des identificateurs entre guillemets permet d'inclure des caractères Unicode échappés en
les identifiant par leur point de code. Cette variante commence par U& (U en majuscule ou minuscule
suivi par un « et commercial ») immédiatement suivis par un guillemet double d'ouverture, sans espace
entre eux. Par exemple U&"foo". (Notez que c'est source d'ambiguïté avec l'opérateur &. Utilisez
les espaces autour de l'opérateur pour éviter ce problème.) À l'intérieur des guillemets, les caractères
Unicode peuvent être indiqués dans une forme échappée en écrivant un antislash suivi par le code
hexadécimal sur quatre chiffres ou, autre possibilité, un antislash suivi du signe plus suivi d'un code
hexadécimal sur six chiffres. Par exemple, l'identificateur "data" peut être écrit ainsi :

U&"d\0061t\+000061"

L'exemple suivant, moins trivial, écrit le mot russe « slon » (éléphant) en lettres cyrilliques :

U&"\0441\043B\043E\043D"

Si un caractère d'échappement autre que l'antislash est désiré, il peut être indiqué en utilisant la clause
UESCAPE après la chaîne. Par exemple :

U&"d!0061t!+000061" UESCAPE '!'

34
Syntaxe SQL

La chaîne d'échappement peut être tout caractère simple autre qu'un chiffre hexadécimal, le signe plus,
un guillemet simple ou double, ou un espace blanc. Notez que le caractère d'échappement est écrit
entre guillemets simples, pas entre guillemets doubles.

Pour inclure le caractère d'échappement dans l'identificateur sans interprétation, écrivez-le deux fois.

La syntaxe d'échappement Unicode fonctionne seulement quand l'encodage serveur est UTF8. Quand
d'autres encodages clients sont utilisés, seuls les codes dans l'échelle ASCII (jusqu'à \007F) peuvent
être utilisés. La forme sur quatre chiffres et la forme sur six chiffres peuvent être utilisées pour indiquer
des paires UTF-16, composant ainsi des caractères comprenant des points de code plus grands que U
+FFFF (et ce, bien que la disponibilité de la forme sur six chiffres ne le nécessite pas techniquement).
(Les paires de substitution ne sont pas stockées directement, mais combinées dans un point de code
seul qui est ensuite encodé en UTF-8.)

Mettre un identificateur entre guillemets le rend sensible à la casse alors que les noms sans guillemets
sont toujours convertis en minuscules. Par exemple, les identificateurs FOO, foo et "foo" sont
considérés identiques par PostgreSQL, mais "Foo" et "FOO" sont différents des trois autres et entre
eux. La mise en minuscule des noms sans guillemets avec PostgreSQL n'est pas compatible avec le
standard SQL qui indique que les noms sans guillemets devraient être mis en majuscule. Du coup,
foo devrait être équivalent à "FOO" et non pas à "foo" en respect avec le standard. Si vous voulez
écrire des applications portables, nous vous conseillons de toujours mettre entre guillemets un nom
particulier ou de ne jamais le mettre.

4.1.2. Constantes
Il existe trois types implicites de constantes dans PostgreSQL : les chaînes, les chaînes de bits et les
nombres. Les constantes peuvent aussi être spécifiées avec des types explicites, ce qui peut activer
des représentations plus précises et gérées plus efficacement par le système. Les constantes implicites
sont décrites ci-dessous ; ces constantes sont discutées dans les sous-sections suivantes.

4.1.2.1. Constantes de chaînes


Une constante de type chaîne en SQL est une séquence arbitraire de caractères entourée par des
guillemets simples ('), par exemple 'Ceci est une chaîne'. Pour inclure un guillemet simple
dans une chaîne constante, saisissez deux guillemets simples adjacents, par exemple 'Le cheval
d''Anne'. Notez que ce n'est pas identique à un guillemet double (").

Deux constantes de type chaîne séparées par un espace blanc avec au moins une nouvelle ligne sont
concaténées et traitées réellement comme si la chaîne avait été écrite dans une constante. Par exemple :

SELECT 'foo'
'bar';

est équivalent à :

SELECT 'foobar';

mais :

SELECT 'foo' 'bar';

n'a pas une syntaxe valide (ce comportement légèrement bizarre est spécifié par le standard SQL ;
PostgreSQL suit le standard).

4.1.2.2. Constantes chaîne avec des échappements de style C


PostgreSQL accepte aussi les constantes de chaîne utilisant des échappements qui sont une extension
au standard SQL. Une constante de type chaîne d'échappement est indiquée en écrivant la lettre
E (en majuscule ou minuscule) juste avant le guillemet d'ouverture, par exemple E'foo'. (Pour
continuer une constante de ce type sur plusieurs lignes, écrire E seulement avant le premier guillemet

35
Syntaxe SQL

d'ouverture.) À l'intérieur d'une chaîne d'échappement, un caractère antislash (\) est géré comme
une séquence d'échappement avec antislash du langage C. La combinaison d'antislash et du (ou des)
caractère(s) suivant(s) représente une valeur spéciale, comme indiqué dans le Tableau 4.1.

Tableau 4.1. Séquences d'échappements avec antislash


Séquence d'échappement avec antislash Interprétation
\b suppression
\f retour en début de ligne
\n saut de ligne
\r saut de ligne
\t tabulation
\o, \oo, \ooo (o = 0 - 7) valeur octale
\xh, \xhh (h = 0 - 9, A - F) valeur hexadécimale
\uxxxx, \Uxxxxxxxx (x = 0 - 9, A - F) caractère Unicode hexadécimal sur 16 ou 32 bits

Tout autre caractère suivi d'un antislash est pris littéralement. Du coup, pour inclure un caractère
antislash, écrivez deux antislashs (\\). De plus, un guillemet simple peut être inclus dans une chaîne
d'échappement en écrivant \', en plus de la façon normale ''.

Il est de votre responsabilité que les séquences d'octets que vous créez, tout spécialement lorsque
vous utilisez les échappements octaux et hexadécimaux, soient des caractères valides dans l'encodage
du jeu de caractères du serveur. Quand l'encodage est UTF-8, alors les échappements Unicode ou
l'autre syntaxe d'échappement Unicode, expliqués dans la Section 4.1.2.3, devraient être utilisés.
(L'alternative serait de réaliser l'encodage UTF-8 manuellement et d'écrire les octets, ce qui serait très
lourd.)

La syntaxe d'échappement Unicode fonctionne complètement, mais seulement quand l'encodage du


serveur est justement UTF8. Lorsque d'autres encodages serveur sont utilisés, seuls les points de code
dans l'échelle ASCII (jusqu'à \u007F) peuvent être utilisés. La forme sur quatre chiffres et la forme
sur six chiffres peuvent être utilisées pour indiquer des paires UTF-16 composant ainsi des caractères
comprenant des points de code plus grands que U+FFFF, et ce bien que la disponibilité de la forme
sur six chiffres ne le nécessite pas techniquement. (Quand des paires de substitution sont utilisées et
que l'encodage du serveur est UTF8, elles sont tout d'abord combinées en un point code seul qui est
ensuite encodé en UTF-8.)

Attention
Si le paramètre de configuration standard_conforming_strings est désactivé (off), alors
PostgreSQL reconnaît les échappements antislashs dans les constantes traditionnelles de type
chaînes et celles échappées. Néanmoins, à partir de PostgreSQL 9.1, la valeur par défaut
est on, ce qui signifie que les échappements par antislash ne sont reconnus que dans les
constantes de chaînes d'échappement. Ce comportement est plus proche du standard SQL, mais
pourrait causer des problèmes aux applications qui se basent sur le comportement historique
où les échappements par antislash étaient toujours reconnus. Pour contourner ce problème,
vous pouvez configurer ce paramètre à off, bien qu'il soit préférable de ne plus utiliser
les échappements par antislash. Si vous avez besoin d'un échappement par antislash pour
représenter un caractère spécial, écrivez la chaîne fixe avec un E.

En plus de standard_conforming_strings, les paramètres de configuration


escape_string_warning et backslash_quote imposent le traitement des antislashs dans les
constantes de type chaîne.

Le caractère de code zéro ne peut pas être placé dans une constante de type chaîne.

36
Syntaxe SQL

4.1.2.3. Constantes de chaînes avec des échappements Unicode


PostgreSQL supporte aussi un autre type de syntaxe d'échappement pour les chaînes qui permettent
d'indiquer des caractères Unicode arbitraires par code. Une constante de chaîne d'échappement
Unicode commence avec U& (U en majuscule ou minuscule suivi par un « et commercial »)
immédiatement suivi par un guillemet double d'ouverture, sans espace entre eux. Par exemple
U&"foo". (Notez que c'est source d'ambiguïté avec l'opérateur &. Utilisez les espaces autour de
l'opérateur pour éviter ce problème.) À l'intérieur des guillemets, les caractères Unicode peuvent être
indiqués dans une forme échappée en écrivant un antislash suivi par le code hexadécimal sur quatre
chiffres ou, autre possibilité, un antislash suivi du signe plus suivi d'un code hexadécimal sur six
chiffres. Par exemple, l'identificateur 'data' peut être écrit ainsi :

U&'d\0061t\+000061'

L'exemple suivant, moins trivial, écrit le mot russe « slon » (éléphant) en lettres cyrilliques :

U&'\0441\043B\043E\043D'

Si un caractère d'échappement autre que l'antislash est souhaité, il peut être indiqué en utilisant la
clause UESCAPE après la chaîne. Par exemple :

U&'d!0061t!+000061' UESCAPE '!'

Le caractère d'échappement peut être tout caractère simple autre qu'un chiffre hexadécimal, le signe
plus, un guillemet simple ou double, ou un espace blanc.

La syntaxe d'échappement Unicode fonctionne seulement quand l'encodage du serveur est UTF8.
Quand d'autres encodages de serveur sont utilisés, seuls les codes dans l'échelle ASCII (jusqu'à
\007F) peuvent être utilisés. La forme sur quatre chiffres et la forme sur six chiffres peuvent être
utilisées pour indiquer des paires de substitution UTF-16, composant ainsi des caractères comprenant
des points de code plus grands que U+FFFF (et ce, bien que la disponibilité de la forme sur six chiffres
ne le nécessite pas techniquement). (Quand des paires de substitution sont utilisées avec un encodage
serveur UTF8, elles sont tout d'abord combinées en un seul point de code, qui est ensuite encodé en
UTF-8.)

De plus, la syntaxe d'échappement de l'Unicode pour les constantes de chaînes fonctionne seulement
quand le paramètre de configuration standard_conforming_strings est activé. Dans le cas contraire,
cette syntaxe est confuse pour les clients qui analysent les instructions SQL, au point que cela pourrait
amener des injections SQL et des problèmes de sécurité similaires. Si le paramètre est désactivé, cette
syntaxe sera rejetée avec un message d'erreur.

Pour inclure le caractère d'échappement littéralement dans la chaîne, écrivez-le deux fois.

4.1.2.4. Constantes de chaînes avec guillemet dollar


Alors que la syntaxe standard pour la spécification des constantes de chaînes est généralement
agréable, elle peut être difficile à comprendre quand la chaîne désirée contient un grand nombre de
guillemets simples car chacun d'entre eux doit être doublé. Pour permettre la saisie de requêtes plus
lisibles dans de telles situations, PostgreSQL fournit une autre façon, appelée « guillemet dollar »,
pour écrire des constantes de chaînes. Une constante de chaîne avec guillemet dollar consiste en un
signe dollar ($), une « balise » optionnelle de zéro ou plus de caractères, un autre signe dollar, une
séquence arbitraire de caractères qui constitue le contenu de la chaîne, un signe dollar, la même balise
et un signe dollar. Par exemple, voici deux façons de spécifier la chaîne « Le cheval d'Anne » en
utilisant les guillemets dollar :

37
Syntaxe SQL

$$Le cheval d'Anne$$


$UneBalise$Le cheval d'Anne$UneBalise$

Notez qu'à l'intérieur de la chaîne avec guillemet dollar, les guillemets simples peuvent être utilisés
sans devoir être échappés. En fait, aucun caractère à l'intérieur d'une chaîne avec guillemet dollar n'a
besoin d'être échappé : le contenu est toujours écrit littéralement. Les antislashs ne sont pas spéciaux,
pas plus que les signes dollar, sauf s'ils font partie d'une séquence correspondant à la balise ouvrante.

Il est possible d'imbriquer les constantes de chaînes avec guillemets dollar en utilisant différentes
balises pour chaque niveau d'imbrication. Ceci est habituellement utilisé lors de l'écriture de définition
de fonctions. Par exemple :

$fonction$
BEGIN
RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
END;
$fonction$

Dans cet exemple, la séquence $q$[\t\r\n\v\\]$q$ représente une chaîne constante avec
guillemet dollar [\t\r\n\v\\], qui sera reconnue quand le corps de la fonction est exécuté par
PostgreSQL. Mais comme la séquence ne correspond pas au délimiteur $fonction$, il s'agit juste
de quelques caractères à l'intérieur de la constante pour ce qu'en sait la chaîne externe.

La balise d'une chaîne avec guillemets dollar, si elle existe, suit les mêmes règles qu'un identificateur
sans guillemets, sauf qu'il ne peut pas contenir de signes dollar. Les balises sont sensibles à la casse, du
coup $balise$Contenu de la chaîne$balise$ est correct, mais $BALISE$Contenu
de la chaîne$balise$ ne l'est pas.

Une chaîne avec guillemets dollar suivant un mot clé ou un identificateur doit en être séparée par un
espace blanc ; sinon, le délimiteur du guillemet dollar serait pris comme faisant partie de l'identificateur
précédent.

Le guillemet dollar ne fait pas partie du standard SQL, mais c'est un moyen bien plus agréable pour
écrire des chaînes constantes que d'utiliser la syntaxe des guillemets simples, bien que compatible avec
le standard. Elle est particulièrement utile pour représenter des constantes de type chaîne à l'intérieur
d'autres constantes, comme cela est souvent le cas avec les définitions de fonctions. Avec la syntaxe
des guillemets simples, chaque antislash dans l'exemple précédent devrait avoir été écrit avec quatre
antislashs, ce qui sera réduit à deux antislashs dans l'analyse de la constante originale, puis à un lorsque
la constante interne est analysée de nouveau lors de l'exécution de la fonction.

4.1.2.5. Constantes de chaînes de bits


Les constantes de chaînes de bits ressemblent aux constantes de chaînes standard avec un B (majuscule
ou minuscule) juste avant le guillemet du début (sans espace blanc), c'est-à-dire B'1001'. Les seuls
caractères autorisés dans les constantes de type chaîne de bits sont 0 et 1.

Les constantes de chaînes de bits peuvent aussi être spécifiées en notation hexadécimale en utilisant un
X avant (minuscule ou majuscule), c'est-à-dire X'1FF'. Cette notation est équivalente à une constante
de chaîne de bits avec quatre chiffres binaires pour chaque chiffre hexadécimal.

Les deux formes de constantes de chaînes de bits peuvent être continuées sur plusieurs lignes de la
même façon que les constantes de chaînes habituelles. Le guillemet dollar ne peut pas être utilisé dans
une constante de chaîne de bits.

4.1.2.6. Constantes numériques


Les constantes numériques sont acceptées dans ces formes générales :

chiffres
chiffres.[chiffres][e[+-]chiffres]

38
Syntaxe SQL

[chiffres].chiffres[e[+-]chiffres]
chiffrese[+-]chiffres

où chiffres est un ou plusieurs chiffres décimaux (de 0 à 9). Au moins un chiffre doit être avant
ou après le point décimal, s'il est utilisé. Au moins un chiffre doit suivre l'indicateur d'exponentielle
(e), s'il est présent. Il ne peut pas y avoir d'espaces ou d'autres caractères imbriqués dans la constante.
Notez que tout signe plus ou moins en avant n'est pas considéré comme faisant part de la constante ;
il est un opérateur appliqué à la constante.

Voici quelques exemples de constantes numériques valides :

42
3.5
4.
.001
5e2
1.925e-3

Une constante numérique ne contenant ni un point décimal ni un exposant est tout d'abord présumée
du type integer si sa valeur est contenue dans le type integer (32 bits) ; sinon, il est présumé
de type bigint si sa valeur entre dans un type bigint (64 bits) ; sinon, il est pris pour un type
numeric. Les constantes contenant des points décimaux et/ou des exposants sont toujours présumées
de type numeric.

Le type de données affecté initialement à une constante numérique est seulement un point de départ
pour les algorithmes de résolution de types. Dans la plupart des cas, la constante sera automatiquement
convertie dans le type le plus approprié suivant le contexte. Si nécessaire, vous pouvez forcer
l'interprétation d'une valeur numérique sur un type de données spécifique en la convertissant. Par
exemple, vous pouvez forcer une valeur numérique à être traitée comme un type real (float4)
en écrivant :

REAL '1.23' -- style chaîne


1.23::REAL -- style PostgreSQL (historique)

Ce sont en fait des cas spéciaux des notations de conversion générales discutées après.

4.1.2.7. Constantes d'autres types


Une constante de type arbitraire peut être saisie en utilisant une des notations suivantes :

type 'chaîne'
'chaîne'::type
CAST ( 'chaîne' AS type )

Le texte de la chaîne constante est passé dans la routine de conversion pour le type appelé type. Le
résultat est une constante du type indiqué. La conversion explicite de type peut être omise s'il n'y a pas
d'ambiguïté sur le type de la constante (par exemple, lorsqu'elle est affectée directement à une colonne
de la table), auquel cas elle est convertie automatiquement.

La constante chaîne peut être écrite en utilisant soit la notation SQL standard soit les guillemets dollar.

Il est aussi possible de spécifier une conversion de type en utilisant une syntaxe style fonction :

nom_type ( 'chaîne' )

mais tous les noms de type ne peuvent pas être utilisés ainsi ; voir la Section 4.2.9 pour plus de détails.

Les syntaxes ::, CAST() et d'appels de fonctions sont aussi utilisables pour spécifier les conversions
de type à l'exécution d'expressions arbitraires, comme discuté dans la Section 4.2.9. Pour éviter une

39
Syntaxe SQL

ambiguïté syntaxique, la syntaxe type 'chaîne' peut seulement être utilisée pour spécifier le
type d'une constante. Une autre restriction sur la syntaxe type 'chaîne' est qu'elle ne fonctionne
pas pour les types de tableau ; utilisez :: ou CAST() pour spécifier le type d'une constante de type
tableau.

La syntaxe de CAST() est conforme au standard SQL. La syntaxe type 'chaine' est une
généralisation du standard : SQL spécifie cette syntaxe uniquement pour quelques types de données,
mais PostgreSQL l'autorise pour tous les types. La syntaxe :: est un usage historique dans
PostgreSQL, comme l'est la syntaxe d'appel de fonction.

4.1.3. Opérateurs
Un nom d'opérateur est une séquence d'au plus NAMEDATALEN-1 (63 par défaut) caractères provenant
de la liste suivante :

+-*/<>=~!@#%^&|`?

Néanmoins, il existe quelques restrictions sur les noms d'opérateurs :

• -- et /* ne peuvent pas apparaître quelque part dans un nom d'opérateur, car ils seront pris pour
le début d'un commentaire.

• Un nom d'opérateur à plusieurs caractères ne peut pas finir avec + ou -, sauf si le nom contient
aussi un de ces caractères :

~!@#%^&|`?

Par exemple, @- est un nom d'opérateur autorisé, mais *- ne l'est pas. Cette restriction permet à
PostgreSQL d'analyser des requêtes compatibles avec SQL sans requérir des espaces entre les jetons.

Lors d'un travail avec des noms d'opérateurs ne faisant pas partie du standard SQL, vous aurez
habituellement besoin de séparer les opérateurs adjacents avec des espaces pour éviter toute ambiguïté.
Par exemple, si vous avez défini un opérateur unaire gauche nommé @, vous ne pouvez pas écrire
X*@Y ; vous devez écrire X* @Y pour vous assurer que PostgreSQL le lit comme deux noms
d'opérateurs, et non pas comme un seul.

4.1.4. Caractères spéciaux


Quelques caractères non alphanumériques ont une signification spéciale, différente de celle d'un
opérateur. Les détails sur leur utilisation sont disponibles à l'endroit où l'élément de syntaxe respectif
est décrit. Cette section existe seulement pour avertir de leur existence et pour résumer le but de ces
caractères.

• Un signe dollar ($) suivi de chiffres est utilisé pour représenter un paramètre de position dans le
corps de la définition d'une fonction ou d'une instruction préparée. Dans d'autres contextes, le signe
dollar pourrait faire partie d'un identificateur ou d'une constante de type chaîne utilisant le dollar
comme guillemet.

• Les parenthèses (()) ont leur signification habituelle pour grouper leurs expressions et renforcer la
précédence. Dans certains cas, les parenthèses sont requises, car faisant partie de la syntaxe d'une
commande SQL particulière.

• Les crochets ([]) sont utilisés pour sélectionner les éléments d'un tableau. Voir la Section 8.15
pour plus d'informations sur les tableaux.

• Les virgules (,) sont utilisées dans quelques constructions syntaxiques pour séparer les éléments
d'une liste.

• Le point-virgule (;) termine une commande SQL. Il ne peut pas apparaître quelque part dans une
commande, sauf à l'intérieur d'une constante de type chaîne ou d'un identificateur entre guillemets.

40
Syntaxe SQL

• Le caractère deux points (:) est utilisé pour sélectionner des « morceaux » de tableaux (voir la
Section 8.15). Dans certains dialectes SQL (tel que le SQL embarqué), il est utilisé pour préfixer
les noms de variables.

• L'astérisque (*) est utilisé dans certains contextes pour indiquer tous les champs de la ligne d'une
table ou d'une valeur composite. Elle a aussi une signification spéciale lorsqu'elle est utilisée comme
argument d'une fonction d'agrégat. Cela signifie que l'agrégat ne requiert pas de paramètre explicite.

• Le point (.) est utilisé dans les constantes numériques et pour séparer les noms de schéma, table
et colonne.

4.1.5. Commentaires
Un commentaire est une séquence de caractères commençant avec deux tirets et s'étendant jusqu'à la
fin de la ligne, par exemple :

-- Ceci est un commentaire standard en SQL

Autrement, les blocs de commentaires style C peuvent être utilisés :

/* commentaires multilignes
* et imbriqués: /* bloc de commentaire imbriqué */
*/

où le commentaire commence avec /* et s'étend jusqu'à l'occurrence de */. Ces blocs de


commentaires s'imbriquent, comme spécifié dans le standard SQL, mais pas comme dans le langage
C. De ce fait, vous pouvez commenter des blocs importants de code pouvant contenir des blocs de
commentaires déjà existants.

Un commentaire est supprimé du flux en entrée avant une analyse plus poussée de la syntaxe et est
remplacé par un espace blanc.

4.1.6. Précédence d'opérateurs


Le Tableau 4.2 affiche la précédence et l'associativité des opérateurs dans PostgreSQL. La plupart des
opérateurs ont la même précédence et sont associatifs par la gauche. La précédence et l'associativité
des opérateurs sont codées en dur dans l'analyseur.

De même, vous aurez quelquefois besoin d'ajouter des parenthèses lors de l'utilisation de combinaisons
d'opérateurs binaires et unaires. Par exemple :

SELECT 5 ! - 6;

sera analysé comme :

SELECT 5 ! (- 6);

parce que l'analyseur n'a aucune idée, jusqu'à ce qu'il ne soit trop tard, que ! est défini comme un
opérateur suffixe, et non pas préfixe. Pour obtenir le comportement désiré dans ce cas, vous devez
écrire :

SELECT (5 !) - 6;

C'est le prix à payer pour l'extensibilité.

Tableau 4.2. Précédence des opérateurs (du plus haut vers le plus bas)
Opérateur/Élément Associativité Description
. gauche séparateur de noms de table et de colonne

41
Syntaxe SQL

Opérateur/Élément Associativité Description


:: gauche conversion de type, style PostgreSQL
[] gauche sélection d'un élément d'un tableau
+- droite plus unaire, moins unaire
^ gauche exposant
*/% gauche multiplication, division, modulo
+- gauche addition, soustraction
(autres opérateurs) gauche tout autre opérateur natif ou défini par l'utilisateur
BETWEEN IN LIKE intervalle contenu, recherche d'appartenance,
ILIKE SIMILAR correspondance de chaîne
< > = <= >= <> opérateurs de comparaison
IS ISNULL NOTNULL IS TRUE, IS FALSE, IS NULL, IS DISTINCT
FROM, etc
NOT droite négation logique
AND gauche conjonction logique
OR gauche disjonction logique

Notez que les règles de précédence des opérateurs s'appliquent aussi aux opérateurs définis par
l'utilisateur qui ont le même nom que les opérateurs internes mentionnés ici. Par exemple, si vous
définissez un opérateur « + » pour un type de données personnalisé, il aura la même précédence que
l'opérateur interne « + », peu importe ce que fait le vôtre.

Lorsqu'un nom d'opérateur qualifié par un schéma est utilisé dans la syntaxe OPERATOR, comme
dans :

SELECT 3 OPERATOR(pg_catalog.+) 4;

la construction OPERATOR est prise pour avoir la précédence par défaut affichée dans le Tableau 4.2
pour les opérateurs « autres ». Ceci est vrai, quel que soit le nom spécifique de l'opérateur apparaissant
à l'intérieur de OPERATOR().

Note
Les versions de PostgreSQL antérieures à la 9.5 utilisaient des règles de précédence différentes
pour les opérateurs. En particulier, <= >= et <> étaient traités comme des opérateurs
génériques ; les tests IS avaient une priorité supérieure ; NOT BETWEEN et les constructions
qui en découlent agissaient de façon incohérente, ayant dans certains cas la précédence de
NOT plutôt que de BETWEEN. Ces règles étaient modifiées pour un meilleur accord avec le
standard SQL et pour réduire la configuration d'un traitement incohérent de constructions
équivalentes logiquement. Dans la plupart des cas, ces changements ne résulteront pas en un
changement de comportement. Il peut arriver que des échecs du type « opérateur inconnu »
surviennent, auquel cas un ajout de parenthèses devrait corriger le problème. Néanmoins, il
existe des cas particuliers où une requête pourrait voir son comportement changé sans qu'une
erreur d'analyse soit renvoyée. Si vous êtes inquiet qu'un de ces changements puisse avoir cassé
quelque chose silencieusement, vous pouvez tester votre application en activant le paramètre
operator_precedence_warning pour voir si des messages d'avertissement sont tracés.

4.2. Expressions de valeurs


Les expressions de valeurs sont utilisées dans une grande variété de contextes, tels que dans la liste
cible d'une commande SELECT, dans les nouvelles valeurs de colonnes d'une commande INSERT ou

42
Syntaxe SQL

UPDATE, ou dans les conditions de recherche d'un certain nombre de commandes. Le résultat d'une
expression de valeurs est quelquefois appelé scalaire, pour le distinguer du résultat d'une expression
de table (qui est une table). Les expressions de valeurs sont aussi appelées des expressions scalaires
(voire simplement des expressions). La syntaxe d'expression permet le calcul des valeurs à partir de
morceaux primitifs en utilisant les opérations arithmétiques, logiques, d'ensemble et autres.

Une expression de valeur peut être :

• une constante ou une valeur constante ;

• une référence de colonne ;

• une référence de la position d'un paramètre, dans le corps d'une définition de fonction ou
d'instruction préparée ;

• une expression indicée ;

• une expression de sélection de champs ;

• un appel d'opérateur ;

• un appel de fonction ;

• une expression d'agrégat ;

• un appel de fonction de fenêtrage ;

• une conversion de type ;

• une expression de collationnement ;

• une sous-requête scalaire ;

• un constructeur de tableau ;

• un constructeur de ligne ;

• toute expression de valeur entre parenthèses, utile pour grouper des sous-expressions et surcharger
la précédence.

En plus de cette liste, il existe un certain nombre de constructions pouvant être classées comme une
expression, mais ne suivant aucune règle de syntaxe générale. Elles ont généralement la sémantique
d'une fonction ou d'un opérateur et sont expliquées au Chapitre 9. Un exemple est la clause IS NULL.

Nous avons déjà discuté des constantes dans la Section 4.1.2. Les sections suivantes discutent des
options restantes.

4.2.1. Références de colonnes


Une colonne peut être référencée avec la forme :

correlation.nom_colonne

correlation est le nom d'une table (parfois qualifié par son nom de schéma) ou un alias d'une
table définie au moyen de la clause FROM. Le nom de corrélation et le point de séparation peuvent
être omis si le nom de colonne est unique dans les tables utilisées par la requête courante (voir aussi
le Chapitre 7).

4.2.2. Paramètres de position


Un paramètre de position est utilisé pour indiquer une valeur fournie en externe par une instruction
SQL. Les paramètres sont utilisés dans des définitions de fonction SQL et dans les requêtes préparées.

43
Syntaxe SQL

Quelques bibliothèques clients supportent aussi la spécification de valeurs de données séparément de


la chaîne de commandes SQL, auquel cas les paramètres sont utilisés pour référencer les valeurs de
données en dehors. Le format d'une référence de paramètre est :

$numéro

Par exemple, considérez la définition d'une fonction : dept :

CREATE FUNCTION dept(text) RETURNS dept


AS $$ SELECT * FROM dept WHERE nom = $1 $$
LANGUAGE SQL;

Dans cet exemple, $1 référence la valeur du premier argument de la fonction à chaque appel de cette
commande.

4.2.3. Indices
Si une expression récupère une valeur de type tableau, alors un élément spécifique du tableau peut
être extrait en écrivant :

expression[indice]

Des éléments adjacents (un « morceau de tableau ») peuvent être extraits en écrivant :

expression[indice_bas:indice_haut]

Les crochets [ ] doivent apparaître réellement. Chaque indice est elle-même une expression, qui
sera arrondie à la valeur entière la plus proche.

En général, l'expression de type tableau doit être entre parenthèses, mais ces dernières peuvent
être omises lorsque l'expression utilisée comme indice est seulement une référence de colonne ou
un paramètre de position. De plus, les indices multiples peuvent être concaténés lorsque le tableau
original est multidimensionnel. Par exemple :

ma_table.colonnetableau[4]
ma_table.colonnes_deux_d[17][34]
$1[10:42]
(fonctiontableau(a,b))[42]

Dans ce dernier exemple, les parenthèses sont requises. Voir la Section 8.15 pour plus d'informations
sur les tableaux.

4.2.4. Sélection de champs


Si une expression récupère une valeur de type composite (type row), alors un champ spécifique de la
ligne est extrait en écrivant :

expression.nom_champ

En général, l'expression de ligne doit être entre parenthèses, mais les parenthèses peuvent être
omises lorsque l'expression à partir de laquelle se fait la sélection est seulement une référence de table
ou un paramètre de position. Par exemple :

ma_table.macolonne
$1.unecolonne
(fonctionligne(a,b)).col3

En fait, une référence de colonne qualifiée est un cas spécial de syntaxe de sélection de champ. Un
cas spécial important revient à extraire un champ de la colonne de type composite d'une table :

44
Syntaxe SQL

(colcomposite).unchamp
(matable.colcomposite).unchamp

Les parenthèses sont requises ici pour montrer que colcomposite est un nom de colonne, et non pas
un nom de table, ou que matable est un nom de table, pas un nom de schéma dans le deuxième cas.

Vous pouvez demander tous les champs d'une valeur composite en écrivant .* :

(compositecol).*

Cette syntaxe se comporte différemment suivant le contexte. Voir Section 8.16.5 pour plus de détails.

4.2.5. Appels d'opérateurs


Il existe trois syntaxes possibles pour l'appel d'un opérateur :

expression opérateur expression (opérateur binaire préfixe)


opérateur expression (opérateur unaire préfixe)
expression opérateur (opérateur unaire suffixe)

où le jeton opérateur suit les règles de syntaxe de la Section 4.1.3, ou est un des mots-clés AND,
OR et NOT, ou est un nom d'opérateur qualifié de la forme

OPERATOR(schema.nom_operateur)

Le fait qu'opérateur particulier existe et qu'il soit unaire ou binaire dépend des opérateurs définis par
le système ou l'utilisateur. Le Chapitre 9 décrit les opérateurs internes.

4.2.6. Appels de fonctions


La syntaxe pour un appel de fonction est le nom d'une fonction (qualifié ou non du nom du schéma)
suivi par sa liste d'arguments entre parenthèses :

nom_fonction([expression [,expression ...]] )

Par exemple, ce qui suit calcule la racine carré de 2 :

sqrt(2)

La liste des fonctions intégrées se trouve dans le Chapitre 9. D'autres fonctions pourraient être ajoutées
par l'utilisateur.

Lors de l'exécution de requêtes dans une base de données où certains utilisateurs ne font pas confiance
aux autres utilisateurs, observez quelques mesures de sécurité disponibles dans Section 10.3 lors de
l'appel de fonctions.

En option, les arguments peuvent avoir leur nom attaché. Voir la Section 4.3 pour les détails.

Note
Une fonction qui prend un seul argument de type composite peut aussi être appelée en utilisant
la syntaxe de sélection de champ. Du coup, un champ peut être écrit dans le style fonctionnel.
Cela signifie que les notations col(table) et table.col sont interchangeables. Ce
comportement ne respecte pas le standard SQL, mais il est fourni dans PostgreSQL, car il

45
Syntaxe SQL

permet l'utilisation de fonctions émulant les « champs calculés ». Pour plus d'informations,
voir la Section 8.16.5.

4.2.7. Expressions d'agrégat


Une expression d'agrégat représente l'application d'une fonction d'agrégat à travers les lignes
sélectionnées par une requête. Une fonction d'agrégat réduit les nombres entrés en une seule valeur de
sortie, comme la somme ou la moyenne des valeurs en entrée. La syntaxe d'une expression d'agrégat
est une des suivantes :

nom_agregat (expression [ , ... ] [ clause_order_by ] ) [ FILTER


( WHERE clause_filtre ) ]
nom_agregat (ALL expression [ , ... ] [ clause_order_by ] )
[ FILTER ( WHERE clause_filtre ) ]
nom_agregat (DISTINCT expression [ , ... ] [ clause_order_by ] )
[ FILTER ( WHERE clause_filtre ) ]
nom_agregat ( * ) [ FILTER ( WHERE clause_filtre ) ]
nom_agregat ( [ expression [ , ... ] ] ) WITHIN GROUP
( clause_order_by ) [ FILTER ( WHERE clause_filtre ) ]

où nom_agregat est un agrégat précédemment défini (parfois qualifié d'un nom de schéma),
expression est toute expression de valeur qui ne contient pas elle-même une expression
d'agrégat ou un appel à une fonction de fenêtrage. Les clauses optionnelles clause_order_by et
clause_filtre sont décrites ci-dessous.

La première forme d'expression d'agrégat appelle l'agrégat une fois pour chaque ligne en entrée. La
seconde forme est identique à la première, car ALL est une clause active par défaut. La troisième
forme fait appel à l'agrégat une fois pour chaque valeur distincte de l'expression (ou ensemble distinct
de valeurs, pour des expressions multiples) trouvée dans les lignes en entrée. La quatrième forme
appelle l'agrégat une fois pour chaque ligne en entrée ; comme aucune valeur particulière en entrée
n'est spécifiée, c'est généralement utile pour la fonction d'agrégat count(*). La dernière forme est
utilisée avec les agrégats à ensemble trié qui sont décrits ci-dessous.

La plupart des fonctions d'agrégats ignorent les entrées NULL, pour que les lignes qui renvoient une
ou plusieurs expressions NULL soient disqualifiées. Ceci peut être considéré comme vrai pour tous
les agrégats internes sauf indication contraire.

Par exemple, count(*) trouve le nombre total de lignes en entrée, alors que count(f1) récupère
le nombre de lignes en entrée pour lesquelles f1 n'est pas NULL. En effet, la fonction count ignore
les valeurs NULL, mais count(distinct f1) retrouve le nombre de valeurs distinctes non NULL
de f1.

D'habitude, les lignes en entrée sont passées à la fonction d'agrégat dans un ordre non spécifié. Dans
la plupart des cas, cela n'a pas d'importance. Par exemple, min donne le même résultat quel que
soit l'ordre dans lequel il reçoit les données. Néanmoins, certaines fonctions d'agrégat (telles que
array_agg et string_agg) donnent un résultat dépendant de l'ordre des lignes en entrée. Lors
de l'utilisation de ce type d'agrégat, la clause clause_order_by peut être utilisée pour préciser
l'ordre de tri désiré. La clause clause_order_by a la même syntaxe que la clause ORDER BY
d'une requête, qui est décrite dans la Section 7.5, sauf que ses expressions sont toujours des expressions
simples et ne peuvent pas être des noms de colonne en sortie ou des numéros. Par exemple :

SELECT array_agg(a ORDER BY b DESC) FROM table;

Lors de l'utilisation de fonctions d'agrégat à plusieurs arguments, la clause ORDER BY arrive après
tous les arguments de l'agrégat. Par exemple, il faut écrire ceci :

SELECT string_agg(a, ',' ORDER BY a) FROM table;

et non pas ceci :

46
Syntaxe SQL

SELECT string_agg(a ORDER BY a, ',') FROM table; -- incorrect

Ce dernier exemple est syntaxiquement correct, mais il concerne un appel à une fonction d'agrégat à
un seul argument avec deux clés pour le ORDER BY (le deuxième étant inutile, car il est constant).

Si DISTINCT est indiqué en plus de la clause clause_order_by, alors toutes les expressions de
l'ORDER BY doivent correspondre aux arguments de l'agrégat ; autrement dit, vous ne pouvez pas
trier sur une expression qui n'est pas incluse dans la liste DISTINCT.

Note
La possibilité de spécifier à la fois DISTINCT et ORDER BY dans une fonction d'agrégat est
une extension de PostgreSQL.

Placer la clause ORDER BY dans la liste des arguments standards de l'agrégat, comme décrit
jusqu'ici, est utilisé pour un agrégat de type général et statistique pour lequel le tri est optionnel.
Il existe une sous-classe de fonctions d'agrégat appelée agrégat d'ensemble trié pour laquelle la
clause clause_order_by est requise, habituellement parce que le calcul de l'agrégat est seulement
sensible à l'ordre des lignes en entrée. Des exemples typiques d'agrégat avec ensemble trié incluent les
calculs de rang et de pourcentage. Pour un agrégat d'ensemble trié, la clause clause_order_by est
écrite à l'intérieur de WITHIN GROUP (...), comme indiqué dans la syntaxe alternative finale. Les
expressions dans clause_order_by sont évaluées une fois par ligne en entrée, comme n'importe
quel argument d'un agrégat, une fois triées suivant la clause clause_order_by, et envoyées à la
fonction en tant qu'arguments en entrée. (Ceci est contraire au cas de la clause clause_order_by
en dehors d'un WITHIN GROUP , qui n'est pas traité comme argument de la fonction d'agrégat.)
Les expressions d'argument précédant WITHIN GROUP, s'il y en a, sont appelées des arguments
directs pour les distinguer des arguments agrégés listés dans clause_order_by. Contrairement
aux arguments normaux d'agrégats, les arguments directs sont évalués seulement une fois par appel
d'agrégat et non pas une fois par ligne en entrée. Cela signifie qu'ils peuvent contenir des variables
seulement si ces variables sont regroupées par GROUP BY ; cette restriction équivaut à des arguments
directs qui ne seraient pas dans une expression d'agrégat. Les arguments directs sont typiquement
utilisés pour des fractions de pourcentage, qui n'ont de sens qu'en tant que valeur singulière par calcul
d'agrégat. La liste d'arguments directs peut être vide ; dans ce cas, écrivez simplement (), et non pas
(*). (PostgreSQL accepte actuellement les deux écritures, mais seule la première est conforme avec
le standard SQL.)

Voici un exemple d'appel d'agrégat à ensemble trié :

SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY revenu) FROM


proprietes;
percentile_cont
-----------------
50489

qui obtient le 50e pourcentage ou le médian des valeurs de la colonne revenu de la table
proprietes. Ici, 0.5 est un argument direct ; cela n'aurait pas de sens si la fraction de pourcentage
était une valeur variant suivant les lignes.

Si la clause FILTER est spécifiée, alors seules les lignes en entrée pour lesquelles filter_clause
est vraie sont envoyées à la fonction d'agrégat ; les autres lignes sont ignorées. Par exemple :

SELECT
count(*) AS nonfiltres,
count(*) FILTER (WHERE i < 5) AS filtres
FROM generate_series(1,10) AS s(i);

47
Syntaxe SQL

nonfiltres | filtres
------------+---------
10 | 4
(1 row)

Les fonctions d'agrégat prédéfinies sont décrites dans la Section 9.20. D'autres fonctions d'agrégat
pourraient être ajoutées par l'utilisateur.

Une expression d'agrégat peut seulement apparaître dans la liste de résultats ou dans la clause HAVING
d'une commande SELECT. Elle est interdite dans d'autres clauses, telles que WHERE, parce que ces
clauses sont logiquement évaluées avant que les résultats des agrégats ne soient calculés.

Lorsqu'une expression d'agrégat apparaît dans une sous-requête (voir la Section 4.2.11 et la
Section 9.22), l'agrégat est normalement évalué sur les lignes de la sous-requête. Cependant,
une exception survient si les arguments de l'agrégat (et clause_filtre si fourni) contiennent
seulement des niveaux externes de variables : ensuite, l'agrégat appartient au niveau externe le plus
proche et est évalué sur les lignes de cette requête. L'expression de l'agrégat est une référence externe
pour la sous-requête dans laquelle il apparaît et agit comme une constante sur toute évaluation de cette
requête. La restriction apparaissant seulement dans la liste de résultats ou dans la clause HAVING
s'applique avec respect du niveau de requête auquel appartient l'agrégat.

4.2.8. Appels de fonction de fenêtrage


Un appel de fonction de fenêtrage représente l'application d'une fonction de type agrégat sur une
portion des lignes sélectionnées par une requête. Contrairement aux appels de fonction d'agrégat
standard, ce n'est pas lié au groupement des lignes sélectionnées en une seule ligne résultat -- chaque
ligne reste séparée dans les résultats. Néanmoins, la fonction de fenêtrage a accès à toutes les lignes
qui font partie du groupe de la ligne courante d'après la spécification du groupe (liste PARTITION
BY) de l'appel de la fonction de fenêtrage. La syntaxe d'un appel de fonction de fenêtrage est une des
suivantes :

nom_fonction ([expression [, expression ... ]]) [ FILTER


( WHERE clause_filtre ) ] OVER nom_window
nom_fonction ([expression [, expression ... ]]) [ FILTER
( WHERE clause_filtre ) ] OVER ( définition_window )
nom_fonction ( * ) [ FILTER ( WHERE clause_filtre ) ]
OVER nom_window
nom_fonction ( * ) [ FILTER ( WHERE clause_filtre ) ] OVER
( définition_window )

où définition_fenêtrage a comme syntaxe :

[ nom_fenêtrage_existante ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING opérateur ] [ NULLS
{ FIRST | LAST } ] [, ...] ]
[ clause_portée ]

et la clause clause_portée optionnelle fait partie de :

{ RANGE | ROWS | GROUPS } début_portée [ frame_exclusion ]


{ RANGE | ROWS | GROUPS } BETWEEN début_portée AND fin_portée
[ frame_exclusion ]

48
Syntaxe SQL

avec début_portée et fin_portée pouvant faire partie de

UNBOUNDED PRECEDING
décalage PRECEDING
CURRENT ROW
décalage FOLLOWING
UNBOUNDED FOLLOWING

et frame_exclusion peut valoir

EXCLUDE CURRENT ROW


EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

Ici, expression représente toute expression de valeur qui ne contient pas elle-même d'appel à des
fonctions de fenêtrage.

nom_fenêtrage est une référence à la spécification d'une fenêtre nommée, définie dans la clause
WINDOW de la requête. Les spécifications de fenêtres nommées sont habituellement référencées avec
OVER nom_fenêtrage, mais il est aussi possible d'écrire un nom de fenêtre entre parenthèses, puis
de fournir en option une clause de tri et/ou une clause de portée (la fenêtre référencée ne doit pas avoir
ces clauses si elles sont fournies ici). Cette dernière syntaxe suit les mêmes règles que la modification
d'un nom de fenêtre existant dans une clause WINDOW ; voir la page de référence de SELECT pour
les détails.

La clause PARTITION BY groupe les lignes de la requête en partitions, qui sont traitées séparément
par la fonction de fenêtrage. PARTITION BY fonctionne de la même façon qu'une clause GROUP BY
au niveau de la requête, sauf que ses expressions sont toujours des expressions et ne peuvent pas être
des noms ou des numéros de colonnes en sortie. Sans PARTITION BY, toutes les lignes produites par
la requête sont traitées comme une seule partition. La clause ORDER BY détermine l'ordre dans lequel
les lignes d'une partition sont traitées par la fonction de fenêtrage. Cela fonctionne de la même façon
que la clause ORDER BY d'une requête, mais ne peut pas non plus utiliser les noms ou les numéros
des colonnes en sortie. Sans ORDER BY, les lignes sont traitées dans n'importe quel ordre.

La clause clause_portée indique l'ensemble de lignes constituant la portée de la fenêtre, qui est
un sous-ensemble de la partition en cours, pour les fonctions de fenêtrage qui agissent sur ce sous-
ensemble plutôt que sur la partition entière. L'ensemble de lignes dans la portée peut varier suivant
la ligne courante. Le sous-ensemble peut être spécifié avec le mode RANGE, avec le mode ROWS
ou avec le mode GROUPS. Dans les deux cas, il s'exécute de début_portée à fin_portée. Si
fin_portée est omis, la fin vaut par défaut CURRENT ROW.

Un début_portée à UNBOUNDED PRECEDING signifie que le sous-ensemble commence avec


la première ligne de la partition. De la même façon, un fin_portée à UNBOUNDED FOLLOWING
signifie que le sous-ensemble se termine avec la dernière ligne de la partition.

Dans les modes RANGE et GROUPS, un début_portée à CURRENT ROW signifie que le sous-
ensemble commence avec la ligne suivant la ligne courante (une ligne que la clause ORDER BY de
la fenêtre considère comme équivalente à la ligne courante), alors qu'un fin_portée à CURRENT
ROW signifie que le sous-ensemble se termine avec la dernière ligne homologue de la ligne en cours.
Dans le mode ROWS, CURRENT ROW signifie simplement la ligne courante.

Dans les options de portée, offset de PRECEDING et offset de FOLLOWING, le offset doit
être une expression ne contenant ni variables, ni fonctions d'agrégat, ni fonctions de fenêtrage. La
signification de offset dépend du mode de porté :

49
Syntaxe SQL

• Dans le mode ROWS, offset doit renvoyer un entier non négatif non NULL, et l'option signifie
que la portée commence ou finit au nombre spécifié de lignes avant ou après la ligne courante.

• Dans le mode GROUPS, offset doit de nouveau renvoyer un entier non négatif non NULL,
et l'option signifie que la portée commence ou finit au nombre spécifié de groupes de lignes
équivalentes avant ou après le groupe de la ligne courante, et où un groupe de lignes équivalentes
est un ensemble de lignes équivalentes dans le tri ORDER BY. (Il doit y avoir une clause ORDER
BY dans la définition de la fenêtre pour utiliser le mode GROUPS.)

• Dans le mode RANGE, ces options requièrent que la clause ORDER BY spécifient exactement une
colonne. offset indique la différence maximale entre la valeur de cette colonne dans la ligne
courante et sa valeur dans les lignes précédentes et suivantes de la portée. Le type de données
de l'expression offset varie suivant le type de données de la colonne triée. Pour les colonnes
ordonnées numériques, il s'agit habituellement du même type que la colonne ordonnée. Mais pour
les colonnes ordonnées de type date/heure, il s'agit d'un interval. Par exemple, si la colonne
ordonnée est de type date ou timestamp, on pourrait écrire RANGE BETWEEN '1 day'
PRECEDING AND '10 days' FOLLOWING. offset est toujours requis pour être non NULL
et non négatif, bien que la signification de « non négatif » dépend de son type de données.

Dans tous les cas, la distance jusqu'à la fin de la portée est limitée par la distance jusqu'à la fin de
la partition, pour que les lignes proche de la fin de la partition, la portée puisse contenir moins de
lignes qu'ailleurs.

Notez que dans les deux modes ROWS et GROUPS, 0 PRECEDING et 0 FOLLOWING sont équivalents
à CURRENT ROW. Le mode RANGE en fait aussi partie habituellement, pour une signification
appropriée de « zéro » pour le type de données spécifique.

L'option frame_exclusion permet aux lignes autour de la ligne courante d'être exclues de la
portée, même si elles seraient incluses d'après les options de début et de fin de portée. EXCLUDE
CURRENT ROW exclut la ligne courante de la portée. EXCLUDE GROUP exclut la ligne courante et
ses équivalents dans l'ordre à partir de la portée. EXCLUDE TIES exclut de la portée tout équivalent
de la ligne courante mais pas la ligne elle-même. EXCLUDE NO OTHERS spécifie explicitement le
comportement par défaut lors de la non exclusion de la ligne courante ou de ses équivalents.

L'option par défaut est RANGE UNBOUNDED PRECEDING, ce qui est identique à RANGE BETWEEN
UNBOUNDED PRECEDING AND CURRENT ROW. Avec ORDER BY, ceci configure le sous-
ensemble pour contenir toutes les lignes de la partition à partir de la ligne courante. Sans ORDER BY,
toutes les lignes de la partition sont incluses dans le sous-ensemble de la fenêtre, car toutes les lignes
deviennent voisines de la ligne en cours.

Les restrictions sont que début_portée ne peut pas valoir UNBOUNDED FOLLOWING,
fin_portée ne peut pas valoir UNBOUNDED PRECEDING, et le choix de fin_portée ne peut
pas apparaître avant la liste ci-dessus des options début_portée et fin_portée que le choix de
frame_start -- par exemple, RANGE BETWEEN CURRENT ROW AND valeur PRECEDING
n'est pas autorisé. Par exemple, ROWS BETWEEN 7 PRECEDING AND 8 PRECEDING est autorisé,
même s'il ne sélectionnera aucune ligne.

Si FILTER est indiqué, seules les lignes en entrée pour lesquelles clause_filtre est vrai sont
envoyées à la fonction de fenêtrage. Les autres lignes sont simplement ignorées. Seules les fonctions
de fenêtrage qui sont des agrégats acceptent une clause FILTER.

Les fonctions de fenêtrage internes sont décrites dans la Tableau 9.57. D'autres fonctions de fenêtrage
peuvent être ajoutées par l'utilisateur. De plus, toute fonction d'agrégat de type général ou statistique
peut être utilisée comme fonction de fenêtrage. Néanmoins, les agrégats d'ensemble trié et d'ensemble
hypothétique ne peuvent pas être utilisés actuellement comme des fonctions de fenêtrage.

Les syntaxes utilisant * sont utilisées pour appeler des fonctions d'agrégats sans paramètres en tant que
fonctions de fenêtrage. Par exemple : count(*) OVER (PARTITION BY x ORDER BY y). Le
symbole * n'est habituellement pas utilisé pour les fonctions de fenêtrage. Les fonctions de fenêtrage
n'autorisent pas l'utilisation de DISTINCT ou ORDER BY dans la liste des arguments de la fonction.

50
Syntaxe SQL

Les appels de fonctions de fenêtrage sont autorisés seulement dans la liste SELECT et dans la clause
ORDER BY de la requête.

Il existe plus d'informations sur les fonctions de fenêtrages dans la Section 3.5, dans la Section 9.21
et dans la Section 7.2.5.

4.2.9. Conversions de type


Une conversion de type spécifie une conversion à partir d'un type de données vers un autre.
PostgreSQL accepte deux syntaxes équivalentes pour les conversions de type :

CAST ( expression AS type )


expression::type

La syntaxe CAST est conforme à SQL ; la syntaxe avec :: est historique dans PostgreSQL.

Lorsqu'une conversion est appliquée à une expression de valeur pour un type connu, il représente une
conversion de type à l'exécution. Cette conversion réussira seulement si une opération convenable de
conversion de type a été définie. Notez que ceci est subtilement différent de l'utilisation de conversion
avec des constantes, comme indiqué dans la Section 4.1.2.7. Une conversion appliquée à une chaîne
constante représente l'affectation initiale d'un type pour une valeur constante, et donc cela réussira
pour tout type (si le contenu de la chaîne constante est une syntaxe acceptée en entrée pour le type
de donnée).

Une conversion de type explicite pourrait être habituellement omise s'il n'y a pas d'ambiguïté sur le type
qu'une expression de valeur pourrait produire (par exemple, lorsqu'elle est affectée à une colonne de
table) ; le système appliquera automatiquement une conversion de type dans de tels cas. Néanmoins, la
conversion automatique est réalisée seulement pour les conversions marquées « OK pour application
implicite » dans les catalogues système. D'autres conversions peuvent être appelées avec la syntaxe
de conversion explicite. Cette restriction a pour but d'empêcher l'exécution silencieuse de conversions
surprenantes.

Il est aussi possible de spécifier une conversion de type en utilisant une syntaxe de type fonction :

nom_type ( expression )

Néanmoins, ceci fonctionne seulement pour les types dont les noms sont aussi valides en tant que
noms de fonctions. Par exemple, double precision ne peut pas être utilisé de cette façon, mais
son équivalent float8 le peut. De même, les noms interval, time et timestamp peuvent
seulement être utilisés de cette façon s'ils sont entre des guillemets doubles, à cause des conflits de
syntaxe. Du coup, l'utilisation de la syntaxe de conversion du style fonction amène à des incohérences
et devrait probablement être évitée.

Note
La syntaxe par fonction est en fait seulement un appel de fonction. Quand un des deux
standards de syntaxe de conversion est utilisé pour faire une conversion à l'exécution, elle
appellera en interne une fonction enregistrée pour réaliser la conversion. Par convention, ces
fonctions de conversion ont le même nom que leur type de sortie et, du coup, la syntaxe
par fonction n'est rien de plus qu'un appel direct à la fonction de conversion sous-jacente.
Évidemment, une application portable ne devrait pas s'y fier. Pour plus d'informations, voir la
page de manuel de CREATE CAST.

4.2.10. Expressions de collationnement


La clause COLLATE surcharge le collationnement d'une expression. Elle est ajoutée à l'expression à
laquelle elle s'applique :

51
Syntaxe SQL

expr COLLATE collationnement

où collationnement est un identificateur pouvant être qualifié par son schéma. La clause
COLLATE a priorité par rapport aux opérateurs ; des parenthèses peuvent être utilisées si nécessaire.

Si aucun collationnement n'est spécifiquement indiqué, le système de bases de données déduit cette
information du collationnement des colonnes impliquées dans l'expression. Si aucune colonne ne se
trouve dans l'expression, il utilise le collationnement par défaut de la base de données.

Les deux utilisations principales de la clause COLLATE sont la surcharge de l'ordre de tri dans une
clause ORDER BY, par exemple :

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

et la surcharge du collationnement d'une fonction ou d'un opérateur qui produit un résultat sensible
à la locale, par exemple :

SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";

Notez que, dans le dernier cas, la clause COLLATE est attachée à l'argument en entrée de l'opérateur.
Peu importe l'argument de l'opérateur ou de la fonction qui a la clause COLLATE, parce que le
collationnement appliqué à l'opérateur ou à la fonction est dérivé en considérant tous les arguments,
et une clause COLLATE explicite surchargera les collationnements des autres arguments. (Attacher
des clauses COLLATE différentes sur les arguments aboutit à une erreur. Pour plus de détails, voir la
Section 23.2.) Du coup, ceci donne le même résultat que l'exemple précédent :

SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';

Mais ceci n'est pas valide :

SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";

car cette requête cherche à appliquer un collationnement au résultat de l'opérateur >, qui est du type
boolean, type non sujet au collationnement.

4.2.11. Sous-requêtes scalaires


Une sous-requête scalaire est une requête SELECT ordinaire entre parenthèses renvoyant exactement
une ligne avec une colonne (voir le Chapitre 7 pour plus d'informations sur l'écriture des requêtes).
La requête SELECT est exécutée et la seule valeur renvoyée est utilisée dans l'expression de valeur
englobante. C'est une erreur d'utiliser une requête qui renvoie plus d'une ligne ou plus d'une colonne
comme requête scalaire. Mais si, lors d'une exécution particulière, la sous-requête ne renvoie pas de
lignes, alors il n'y a pas d'erreur ; le résultat scalaire est supposé NULL. La sous-requête peut référencer
des variables de la requête englobante, qui agiront comme des constantes durant toute évaluation de
la sous-requête. Voir aussi la Section 9.22 pour d'autres expressions impliquant des sous-requêtes.

Par exemple, ce qui suit trouve la ville disposant de la population la plus importante dans chaque état :

SELECT nom, (SELECT max(pop) FROM villes WHERE villes.etat =


etat.nom)
FROM etats;

4.2.12. Constructeurs de tableaux


Un constructeur de tableau est une expression qui construit une valeur de tableau à partir de la valeur de
ses membres. Un constructeur de tableau simple utilise le mot-clé ARRAY, un crochet ouvrant [, une
liste d'expressions (séparées par des virgules) pour les valeurs des éléments du tableau et finalement
un crochet fermant ]. Par exemple :

52
Syntaxe SQL

SELECT ARRAY[1,2,3+4];
array
---------
{1,2,7}
(1 row)

Par défaut, le type d'élément du tableau est le type commun des expressions des membres, déterminé
en utilisant les mêmes règles que pour les constructions UNION ou CASE (voir la Section 10.5). Vous
pouvez surcharger ceci en convertissant explicitement le constructeur de tableau vers le type désiré.
Par exemple :

SELECT ARRAY[1,2,22.7]::integer[];
array
----------
{1,2,23}
(1 row)

Ceci a le même effet que la conversion de chaque expression vers le type d'élément du tableau
individuellement. Pour plus d'informations sur les conversions, voir la Section 4.2.9.

Les valeurs de tableaux multidimensionnels peuvent être construites par des constructeurs de tableaux
imbriqués. Pour les constructeurs internes, le mot-clé ARRAY peut être omis. Par exemple, ces
expressions produisent le même résultat :

SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];


array
---------------
{{1,2},{3,4}}
(1 row)

SELECT ARRAY[[1,2],[3,4]];
array
---------------
{{1,2},{3,4}}
(1 row)

Comme les tableaux multidimensionnels doivent être rectangulaires, les constructeurs internes
du même niveau doivent produire des sous-tableaux de dimensions identiques. Toute conversion
appliquée au constructeur ARRAY externe se propage automatiquement à tous les constructeurs
internes.

Les éléments d'un constructeur de tableau multidimensionnel peuvent être tout ce qui récupère un
tableau du bon type, pas seulement une construction d'un tableau imbriqué. Par exemple :

CREATE TABLE tab(f1 int[], f2 int[]);

INSERT INTO tab VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]);

SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM tab;


array
------------------------------------------------
{{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
(1 row)

Vous pouvez construire un tableau vide, mais comme il est impossible d'avoir un tableau sans type,
vous devez convertir explicitement votre tableau vide dans le type désiré. Par exemple :

SELECT ARRAY[]::integer[];

53
Syntaxe SQL

array
-------
{}
(1 row)

Il est aussi possible de construire un tableau à partir des résultats d'une sous-requête. Avec cette forme,
le constructeur de tableau est écrit avec le mot-clé ARRAY suivi par une sous-requête entre parenthèses
(et non pas des crochets). Par exemple :

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');


array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
(1 row)

SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS


a(i));
array
----------------------------------
{{1,2},{2,4},{3,6},{4,8},{5,10}}
(1 row)

La sous-requête doit renvoyer une seule colonne. Si la sortie de la sous- requête n'est pas de type
tableau, le tableau à une dimension résultant aura un élément pour chaque ligne dans le résultat de la
sous-requête, avec un type élément correspondant à celui de la colonne en sortie de la sous- requête. Si
la colonne en sortie de la sous-requête est de type tableau, le résultat sera un tableau du même type, mais
avec une dimension supplémentaire ; dans ce cas, toutes les lignes de la sous-requête doivent renvoyer
des tableaux de dimension identique (dans le cas contraire, le résultat ne serait pas rectangulaire).

Les indices d'un tableau construit avec ARRAY commencent toujours à un. Pour plus d'informations
sur les tableaux, voir la Section 8.15.

4.2.13. Constructeurs de lignes


Un constructeur de ligne est une expression qui construit une valeur de ligne (aussi appelée une valeur
composite) à partir des valeurs de ses membres. Un constructeur de ligne consiste en un mot-clé ROW,
une parenthèse gauche, zéro ou une ou plus d'une expression (séparées par des virgules) pour les
valeurs des champs de la ligne, et enfin une parenthèse droite. Par exemple :

SELECT ROW(1,2.5,'ceci est un test');

Le mot-clé ROW est optionnel lorsqu'il y a plus d'une expression dans la liste.

Un constructeur de ligne peut inclure la syntaxe valeurligne.*, qui sera étendue en une liste
d'éléments de la valeur ligne, ce qui est le comportement habituel de la syntaxe .* utilisée au niveau
haut d'une liste SELECT (voir Section 8.16.5). Par exemple, si la table t a les colonnes f1 et f2, ces
deux requêtes sont identiques :

SELECT ROW(t.*, 42) FROM t;


SELECT ROW(t.f1, t.f2, 42) FROM t;

Note
Avant PostgreSQL 8.2, la syntaxe .* n'était pas étendue dans les constructeurs de lignes. De ce
fait, ROW(t.*, 42) créait une ligne à deux champs dont le premier était une autre valeur de
ligne. Le nouveau comportement est généralement plus utile. Si vous avez besoin de l'ancien

54
Syntaxe SQL

comportement de valeurs de ligne imbriquées, écrivez la valeur de ligne interne sans .*, par
exemple ROW(t, 42).

Par défaut, la valeur créée par une expression ROW est d'un type d'enregistrement anonyme. Si
nécessaire, il peut être converti en un type composite nommé -- soit le type de ligne d'une table, soit
un type composite créé avec CREATE TYPE AS. Une conversion explicite pourrait être nécessaire
pour éviter toute ambiguïté. Par exemple :

CREATE TABLE ma_table(f1 int, f2 float, f3 text);

CREATE FUNCTION recup_f1(ma_table) RETURNS int AS 'SELECT $1.f1'


LANGUAGE SQL;

-- Aucune conversion nécessaire parce que seul un recup_f1() existe


SELECT recup_f1(ROW(1,2.5,'ceci est un test'));
recup_f1
----------
1
(1 row)

CREATE TYPE mon_typeligne AS (f1 int, f2 text, f3 numeric);

CREATE FUNCTION recup_f1(mon_typeligne) RETURNS int AS 'SELECT


$1.f1' LANGUAGE SQL;

-- Maintenant, nous avons besoin d'une conversion


-- pour indiquer la fonction à appeler
SELECT recup_f1(ROW(1,2.5,'ceci est un test'));
ERROR: function recup_f1(record) is not unique

SELECT recup_f1(ROW(1,2.5,'ceci est un test')::ma_table);


getf1
-------
1
(1 row)

SELECT recup_f1(CAST(ROW(11,'ceci est un test',2.5) AS


mon_typeligne));
getf1
-------
11
(1 row)

Les constructeurs de lignes peuvent être utilisés pour construire des valeurs composites à stocker dans
une colonne de table de type composite ou pour être passés à une fonction qui accepte un paramètre
composite. De plus, il est possible de comparer deux valeurs de lignes ou de tester une ligne avec IS
NULL ou IS NOT NULL, par exemple

SELECT ROW(1,2.5,'ceci est un test') = ROW(1, 3, 'pas le même');

SELECT ROW(table.*) IS NULL FROM table; -- détecte toutes les


lignes non NULL

Pour plus de détails, voir la Section 9.23. Les constructeurs de lignes peuvent aussi être utilisés en
relation avec des sous-requêtes, comme discuté dans la Section 9.22.

4.2.14. Règles d'évaluation des expressions

55
Syntaxe SQL

L'ordre d'évaluation des sous-expressions n'est pas défini. En particulier, les entrées d'un opérateur
ou d'une fonction ne sont pas obligatoirement évaluées de la gauche vers la droite ou dans un autre
ordre fixé.

De plus, si le résultat d'une expression peut être déterminé par l'évaluation de certaines parties de celle-
ci, alors d'autres sous-expressions devraient ne pas être évaluées du tout. Par exemple, si vous écrivez :

SELECT true OR une_fonction();

alors une_fonction() pourrait (probablement) ne pas être appelée du tout. Pareil dans le cas
suivant :

SELECT une_fonction() OR true;

Notez que ceci n'est pas identique au « court-circuitage » de gauche à droite des opérateurs booléens
utilisé par certains langages de programmation.

En conséquence, il est déconseillé d'utiliser des fonctions ayant des effets de bord dans une partie des
expressions complexes. Il est particulièrement dangereux de se fier aux effets de bord ou à l'ordre
d'évaluation dans les clauses WHERE et HAVING, car ces clauses sont reproduites de nombreuses fois
lors du développement du plan d'exécution. Les expressions booléennes (combinaisons AND/OR/NOT)
dans ces clauses pourraient être réorganisées d'une autre façon autorisée dans l'algèbre booléenne.

Quand il est essentiel de forcer l'ordre d'évaluation, une construction CASE (voir la Section 9.17) peut
être utilisée. Voici un exemple qui ne garantit pas qu'une division par zéro ne soit pas faite dans une
clause WHERE :

SELECT ... WHERE x > 0 AND y/x > 1.5;

Mais ceci est sûr :

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

Une construction CASE utilisée de cette façon déjouera les tentatives d'optimisation, donc cela ne sera
à faire que si c'est nécessaire (dans cet exemple particulier, il serait sans doute mieux de contourner
le problème en écrivant y > 1.5*x).

Néanmoins, CASE n'est pas un remède à tout. Une limitation à la technique illustrée ci-dessus est
qu'elle n'empêche pas l'évaluation en avance des sous-expressions constantes. Comme décrit dans
Section 38.7, les fonctions et les opérateurs marqués IMMUTABLE peuvent être évalués quand la
requête est planifiée plutôt que quand elle est exécutée. Donc, par exemple :

SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;

va produire comme résultat un échec pour division par zéro, car le planificateur a essayé de simplifier
la sous-expression constante, même si chaque ligne de la table a x > 0 de façon à ce que la condition
ELSE ne soit jamais exécutée.

Bien que cet exemple particulier puisse sembler stupide, il existe de nombreux cas moins évidents,
n'impliquant pas de constantes, mais plutôt des requêtes exécutées par des fonctions, quand les valeurs
des arguments des fonctions et de variables locales peuvent être insérées dans les requêtes en tant que
constantes toujours dans le but de la planification. À l'intérieur de fonctions PL/pgSQL, par exemple,
utiliser une instruction IF-THEN- ELSE pour protéger un calcul risqué est beaucoup plus sûr qu'une
expression CASE.

Une autre limitation de cette technique est qu'une expression CASE ne peut pas empêcher l'évaluation
d'une expression d'agrégat contenue dans cette expression, car les expressions d'agrégat sont calculées
avant les expressions « scalaires » dans une liste SELECT ou dans une clause HAVING. Par exemple,
la requête suivante peut provoquer une erreur de division par zéro bien qu'elle semble protégée contre
ce type d'erreurs :

56
Syntaxe SQL

SELECT CASE WHEN min(employees) > 0


THEN avg(expenses / employees)
END
FROM departments;

Les agrégats min() et avg() sont calculés en même temps avec toutes les lignes en entrée, donc si
une ligne a une valeur 0 pour la colonne employees, l'erreur de division par zéro surviendra avant
d'avoir pu tester le résultat de min(). Il est préférable d'utiliser une clause WHERE ou une clause
FILTER pour empêcher les lignes problématiques en entrée d'atteindre la fonction d'agrégat.

4.3. Fonctions appelantes


PostgreSQL permet aux fonctions qui ont des paramètres nommés d'être appelées en utilisant soit la
notation par position soit la notation par nom. La notation par nom est particulièrement utile pour
les fonctions qui ont un grand nombre de paramètres, car elle rend l'association entre paramètre et
argument plus explicite et fiable. Dans la notation par position, un appel de fonction précise les valeurs
en argument dans le même ordre que ce qui a été défini à la création de la fonction. Dans la notation
nommée, les arguments sont précisés par leur nom et peuvent du coup être intégrés dans n'importe quel
ordre. Pour chaque notation, considérez aussi l'effet des types d'argument de la fonction, documenté
dans Section 10.3.

Quel que soit la notation, les paramètres qui ont des valeurs par défaut dans leur déclaration n'ont
pas besoin d'être précisés dans l'appel. Ceci est particulièrement utile dans la notation nommée, car
toute combinaison de paramètre peut être omise, alors que dans la notation par position, les paramètres
peuvent seulement être omis de la droite vers la gauche.

PostgreSQL supporte aussi la notation mixée. Elle combine la notation par position avec la notation
par nom. Dans ce cas, les paramètres de position sont écrits en premier, les paramètres nommés
apparaissent après.

Les exemples suivants illustrent l'utilisation des trois notations, en utilisant la définition de fonction
suivante :

CREATE FUNCTION assemble_min_ou_maj(a text, b text, majuscule


boolean DEFAULT false)
RETURNS text
AS
$$
SELECT CASE
WHEN $3 THEN UPPER($1 || ' ' || $2)
ELSE LOWER($1 || ' ' || $2)
END;
$$
LANGUAGE SQL IMMUTABLE STRICT;

La fonction assemble_min_ou_maj a deux paramètres obligatoires, a et b. Il existe en plus


un paramètre optionnel, majuscule, qui vaut par défaut false. Les arguments a et b seront
concaténés et forcés soit en majuscule soit en minuscule suivant la valeur du paramètre majuscule.
Les détails restants ne sont pas importants ici (voir le Chapitre 38 pour plus d'informations).

4.3.1. En utilisant la notation par position


La notation par position est le mécanisme traditionnel pour passer des arguments aux fonctions avec
PostgreSQL. En voici un exemple :

57
Syntaxe SQL

SELECT assemble_min_ou_maj('Hello', 'World', true);


assemble_min_ou_maj
---------------------
HELLO WORLD
(1 row)

Tous les arguments sont indiqués dans l'ordre. Le résultat est en majuscule, car l'argument
majuscule est indiqué à true. Voici un autre exemple :

SELECT assemble_min_ou_maj('Hello', 'World');


assemble_min_ou_maj
-----------------------
hello world
(1 row)

Ici, le paramètre majuscule est omis, donc il récupère la valeur par défaut, soit false, ce qui a
pour résultat une sortie en minuscule. Dans la notation par position, les arguments peuvent être omis
de la droite à la gauche à partir du moment où ils ont des valeurs par défaut.

4.3.2. En utilisant la notation par nom


Dans la notation par nom, chaque nom d'argument est précisé en utilisant => pour le séparer de
l'expression de la valeur de l'argument. Par exemple :

SELECT assemble_min_ou_maj(a => 'Hello', b => 'World');


assemble_min_ou_maj
---------------------
hello world
(1 row)

Encore une fois, l'argument majuscule a été omis, donc il dispose de sa valeur par défaut, false,
implicitement. Un avantage à utiliser la notation par nom est que les arguments peuvent être saisis
dans n'importe quel ordre. Par exemple :

SELECT assemble_min_ou_maj(a => 'Hello', b => 'World', uppercase =>


true);
assemble_min_ou_maj
---------------------
HELLO WORLD
(1 row)

SELECT assemble_min_ou_maj(a => 'Hello', uppercase => true, b =>


'World');
assemble_min_ou_maj
---------------------
HELLO WORLD
(1 row)

Une syntaxe plus ancienne basée sur « := » est supportée pour des raisons de compatibilité ascendante :

58
Syntaxe SQL

SELECT assemble_min_ou_maj(a := 'Hello', uppercase := true, b :=


'World');
assemble_min_ou_maj
---------------------
HELLO WORLD
(1 row)

4.3.3. En utilisant la notation mixée


La notation mixée combine les notations par position et par nom. Néanmoins, comme cela a déjà été
expliqué, les arguments par nom ne peuvent pas précéder les arguments par position. Par exemple :

SELECT assemble_min_ou_maj('Hello', 'World', majuscule => true);


assemble_min_ou_maj
-----------------------
HELLO WORLD
(1 row)

Dans la requête ci-dessus, les arguments a et b sont précisés par leur position alors que majuscule
est indiqué par son nom. Dans cet exemple, cela n'apporte pas grand-chose, sauf pour une
documentation de la fonction. Avec une fonction plus complexe, comprenant de nombreux paramètres
avec des valeurs par défaut, les notations par nom et mixées améliorent l'écriture des appels de fonction
et permettent de réduire les risques d'erreurs.

Note
Les notations par appel nommé ou mixe ne peuvent pas être utilisées lors de l'appel d'une
fonction d'agrégat (mais elles fonctionnent quand une fonction d'agrégat est utilisée en tant
que fonction de fenêtrage).

59
Chapitre 5. Définition des données
Ce chapitre couvre la création des structures de données amenées à contenir les données. Dans une
base relationnelle, les données brutes sont stockées dans des tables. De ce fait, une grande partie
de ce chapitre est consacrée à l'explication de la création et de la modification des tables et aux
fonctionnalités disponibles pour contrôler les données stockées dans les tables. L'organisation des
tables dans des schémas et l'attribution de privilèges sur les tables sont ensuite décrites. Pour finir,
d'autres fonctionnalités, telles que l'héritage, le partitionnement de tables, les vues, les fonctions et les
triggers sont passés en revue.

5.1. Notions fondamentales sur les tables


Une table dans une base relationnelle ressemble beaucoup à un tableau sur papier : elle est constituée
de lignes et de colonnes. Le nombre et l'ordre des colonnes sont fixes et chaque colonne a un nom. Le
nombre de lignes est variable -- il représente le nombre de données stockées à un instant donné. Le
SQL n'apporte aucune garantie sur l'ordre des lignes dans une table. Quand une table est lue, les lignes
apparaissent dans un ordre non spécifié, sauf si un tri est demandé explicitement. Tout cela est expliqué
dans le Chapitre 7. De plus, le SQL n'attribue pas d'identifiant unique aux lignes. Il est donc possible
d'avoir plusieurs lignes identiques au sein d'une table. C'est une conséquence du modèle mathématique
sur lequel repose le SQL, même si cela n'est habituellement pas souhaitable. Il est expliqué plus bas
dans ce chapitre comment traiter ce problème.

Chaque colonne a un type de données. Ce type limite l'ensemble de valeurs qu'il est possible d'attribuer
à une colonne. Il attribue également une sémantique aux données stockées dans la colonne pour
permettre les calculs sur celles-ci. Par exemple, une colonne déclarée dans un type numérique n'accepte
pas les chaînes textuelles ; les données stockées dans une telle colonne peuvent être utilisées dans
des calculs mathématiques. Par opposition, une colonne déclarée de type chaîne de caractères accepte
pratiquement n'importe quel type de donnée, mais ne se prête pas aux calculs mathématiques. D'autres
types d'opérations, telle la concaténation de chaînes, sont cependant disponibles.

PostgreSQL inclut un ensemble important de types de données intégrés pour s'adapter à diverses
applications. Les utilisateurs peuvent aussi définir leurs propres types de données.

La plupart des types de données intégrés ont des noms et des sémantiques évidents. C'est pourquoi
leur explication détaillée est reportée au Chapitre 8.

Parmi les types les plus utilisés, on trouve integer pour les entiers, numeric pour les éventuelles
fractions, text pour les chaînes de caractères, date pour les dates, time pour les heures et
timestamp pour les valeurs qui contiennent à la fois une date et une heure.

Pour créer une table, on utilise la commande bien nommée CREATE TABLE. Dans cette commande,
il est nécessaire d'indiquer, au minimum, le nom de la table, les noms des colonnes et le type de
données de chacune d'elles. Par exemple :

CREATE TABLE ma_premiere_table (


premiere_colonne text,
deuxieme_colonne integer
);

Cela crée une table nommée ma_premiere_table avec deux colonnes. La première
colonne, nommée premiere_colonne, est de type text ; la seconde colonne, nommée
deuxieme_colonne, est de type integer. Les noms des tables et colonnes se conforment à la
syntaxe des identifiants expliquée dans la Section 4.1.1. Les noms des types sont souvent aussi des
identifiants, mais il existe des exceptions. Le séparateur de la liste des colonnes est la virgule. La liste
doit être entre parenthèses.

60
Définition des données

L'exemple qui précède est à l'évidence extrêmement simpliste. On donne habituellement aux tables
et aux colonnes des noms qui indiquent les données stockées. L'exemple ci-dessous est un peu plus
réaliste :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric
);

(Le type numeric peut stocker des fractions telles que les montants.)

Astuce
Quand de nombreuses tables liées sont créées, il est préférable de définir un motif cohérent
pour le nommage des tables et des colonnes. On a ainsi la possibilité d'utiliser le pluriel ou le
singulier des noms, chacune ayant ses fidèles et ses détracteurs.

Le nombre de colonnes d'une table est limité. En fonction du type de colonnes, il oscille entre 250
et 1600. Définir une table avec un nombre de colonnes proche de cette limite est, cependant, très
inhabituel et doit conduire à se poser des questions quant à la conception du modèle.

Lorsqu'une table n'est plus utile, elle peut être supprimée à l'aide de la commande DROP TABLE.
Par exemple :

DROP TABLE ma_premiere_table;


DROP TABLE produits;

Tenter de supprimer une table qui n'existe pas lève une erreur. Il est néanmoins habituel, dans les
fichiers de scripts SQL, d'essayer de supprimer chaque table avant de la créer. Les messages d'erreur
sont alors ignorés afin que le script fonctionne, que la table existe ou non. (La variante DROP TABLE
IF EXISTS peut aussi être utilisée pour éviter les messages d'erreur, mais elle ne fait pas partie du
standard SQL.)

Pour la procédure de modification d'une table qui existe déjà, voir la Section 5.5 plus loin dans ce
chapitre.

Les outils précédemment décrits permettent de créer des tables fonctionnelles. Le reste de ce chapitre
est consacré à l'ajout de fonctionnalités à la définition de tables pour garantir l'intégrité des données,
la sécurité ou l'ergonomie. Le lecteur impatient d'insérer des données dans ses tables peut sauter au
Chapitre 6 et lire le reste de ce chapitre plus tard.

5.2. Valeurs par défaut


Une valeur par défaut peut être attribuée à une colonne. Quand une nouvelle ligne est créée et qu'aucune
valeur n'est indiquée pour certaines de ses colonnes, celles-ci sont remplies avec leurs valeurs par
défaut respectives. Une commande de manipulation de données peut aussi demander explicitement
que la valeur d'une colonne soit positionnée à la valeur par défaut, sans qu'il lui soit nécessaire de
connaître cette valeur (les détails concernant les commandes de manipulation de données sont donnés
dans le Chapitre 6).

Si aucune valeur par défaut n'est déclarée explicitement, la valeur par défaut est la valeur NULL. Cela
a un sens dans la mesure où l'on peut considérer que la valeur NULL représente des données inconnues.

Dans la définition d'une table, les valeurs par défaut sont listées après le type de données de la colonne.
Par exemple:

61
Définition des données

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric DEFAULT 9.99
);

La valeur par défaut peut être une expression, alors évaluée à l'insertion de cette valeur (pas à la
création de la table). Un exemple commun est la colonne de type timestamp dont la valeur par défaut
est now(). Elle se voit ainsi attribuer l'heure d'insertion. Un autre exemple est la génération d'un
« numéro de série » pour chaque ligne. Dans PostgreSQL, cela s'obtient habituellement par quelque
chose comme

CREATE TABLE produits (


no_produit integer DEFAULT nextval('produits_no_produit_seq'),
...
);

où la fonction nextval() fournit des valeurs successives à partir d'un objet séquence (voir la
Section 9.16). Cet arrangement est suffisamment commun pour qu'il ait son propre raccourci :

CREATE TABLE produits (


no_produit SERIAL,
...
);

Le raccourci SERIAL est discuté plus tard dans la Section 8.1.4.

5.3. Contraintes
Les types de données sont un moyen de restreindre la nature des données qui peuvent être stockées dans
une table. Pour beaucoup d'applications, toutefois, la contrainte fournie par ce biais est trop grossière.
Par exemple, une colonne qui contient le prix d'un produit ne doit accepter que des valeurs positives.
Mais il n'existe pas de type de données standard qui n'accepte que des valeurs positives. Un autre
problème peut provenir de la volonté de contraindre les données d'une colonne par rapport aux autres
colonnes ou lignes. Par exemple, dans une table contenant des informations de produit, il ne peut y
avoir qu'une ligne par numéro de produit.

Pour cela, SQL permet de définir des contraintes sur les colonnes et les tables. Les contraintes donnent
autant de contrôle sur les données des tables qu'un utilisateur peut le souhaiter. Si un utilisateur tente
de stocker des données dans une colonne en violation d'une contrainte, une erreur est levée. Cela
s'applique même si la valeur vient de la définition de la valeur par défaut.

5.3.1. Contraintes de vérification


La contrainte de vérification est la contrainte la plus générique qui soit. Elle permet d'indiquer que
la valeur d'une colonne particulière doit satisfaire une expression booléenne (valeur de vérité). Par
exemple, pour obliger les prix des produits à être positifs, on peut utiliser :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric CHECK (prix > 0)
);

La définition de contrainte vient après le type de données, comme pour les définitions de valeur par
défaut. Les valeurs par défaut et les contraintes peuvent être données dans n'importe quel ordre. Une
contrainte de vérification s'utilise avec le mot-clé CHECK suivi d'une expression entre parenthèses.
L'expression de la contrainte implique habituellement la colonne à laquelle elle s'applique, la contrainte
n'ayant dans le cas contraire que peu de sens.

62
Définition des données

La contrainte peut prendre un nom distinct. Cela clarifie les messages d'erreur et permet de faire
référence à la contrainte lorsqu'elle doit être modifiée. La syntaxe est :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric CONSTRAINT prix_positif CHECK (prix > 0)
);

Pour indiquer une contrainte nommée, on utilise le mot-clé CONSTRAINT suivi d'un identifiant et de
la définition de la contrainte (si aucun nom n'est précisé, le système en choisit un).

Une contrainte de vérification peut aussi faire référence à plusieurs colonnes. Dans le cas d'un produit,
on peut vouloir stocker le prix normal et un prix réduit en s'assurant que le prix réduit soit bien inférieur
au prix normal.

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric CHECK (prix > 0),
prix_promotion numeric CHECK (prix_promotion > 0),
CHECK (prix > prix_promotion)
);

Si les deux premières contraintes n'offrent pas de nouveauté, la troisième utilise une nouvelle syntaxe.
Elle n'est pas attachée à une colonne particulière, mais apparaît comme un élément distinct dans la
liste des colonnes. Les définitions de colonnes et ces définitions de contraintes peuvent être définies
dans un ordre quelconque.

Les deux premières contraintes sont appelées contraintes de colonne, tandis que la troisième est
appelée contrainte de table parce qu'elle est écrite séparément d'une définition de colonne particulière.
Les contraintes de colonne peuvent être écrites comme des contraintes de table, mais l'inverse n'est pas
forcément possible puisqu'une contrainte de colonne est supposée ne faire référence qu'à la colonne
à laquelle elle est attachée (PostgreSQL ne vérifie pas cette règle, mais il est préférable de la suivre
pour s'assurer que les définitions de tables fonctionnent avec d'autres systèmes de bases de données).
L'exemple ci-dessus peut aussi s'écrire :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric,
CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0),
CHECK (prix > prix_promotion)
);

ou même :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0 AND prix > prix_promotion)
);

C'est une question de goût.

63
Définition des données

Les contraintes de table peuvent être nommées, tout comme les contraintes de colonne :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric,
CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0),
CONSTRAINT promo_valide CHECK (prix > prix_promotion)
);

Une contrainte de vérification est satisfaite si l'expression est évaluée vraie ou NULL. Puisque la
plupart des expressions sont évaluées NULL si l'un des opérandes est nul, elles n'interdisent pas les
valeurs NULL dans les colonnes contraintes. Pour s'assurer qu'une colonne ne contient pas de valeurs
NULL, la contrainte NOT NULL décrite dans la section suivante peut être utilisée.

Note
PostgreSQL ne supporte pas les contraintes CHECK qui référencent les données d'autres tables
que celle contenant la nouvelle ligne ou la ligne mise à jour en cours de vérification. Alors
qu'une contrainte CHECK qui viole cette règle pourrait apparaitre fonctionner dans des tests
simples, il est possible que la base de données atteigne un état dans lequel la condiction
de la contrainte est fausse (à cause de changements supplémentaires en dehors de la ligne
impliquée). Ceci sera la cause d'un échec du rechargement de la sauvegarde d'une base.
La restauration pourrait échouer même quand l'état complet de la base est cohérent avec la
contrainte, à cause de lignes chargées dans un autre différent qui satisferait la contrainte. Si
possible, utilisez les contraintes UNIQUE, EXCLUDE, et FOREIGN KEY pour exprimer des
restrictions inter-lignes et inter-tables.

Si ce que vous désirez est une vérification unique avec certaines lignes au moment
de l'insertion, plutôt qu'une garantie de cohérence maintenue en permanence, un trigger
personnalisé peut être utilisé pour l'implémenter. (Cette approche évite le problème de
sauvegarde/restauration car pg_dump ne réinstalle les triggers qu'après chargement des
données, donc cette vérification ne sera pas effectuée pendant une sauvegarde/restauration.)

Note
PostgreSQL suppose que les conditions des contraintes CHECK sont immutables, c'est-à-dire
qu'elles donneront toujours le même résultat pour la même ligne en entrée. Cette supposition
est ce qui justifie l'examen des contraintes CHECK uniquement quand les lignes sont insérées
ou mises à jour, et non pas à d'autres moments. (Cet avertissement sur la non référence aux
données d'autres tables est en fait un cas particulier de cette restriction.)

Un exemple d'une façon habituelle de casser cette supposition est de référencer une fonction
utilisateur dans une expression CHECK, puis de changer le comportement de cette fonction.
PostgreSQL n'interdit pas cela, mais il ne notera pas qu'il y a des lignes dans la table qui
violent maintenant la contrainte CHECK. Ceci sera la cause d'un échec de la restauration d'une
sauvegarde de cette base. La façon recommandée de gérer de tels changements revient à
supprimer la contrainte (en utilisant ALTER TABLE), d'ajuster la définition de la fonction,
et d'ajouter de nouveau la contrainte, ce qui causera une nouvelle vérification des lignes de
la table.

5.3.2. Contraintes de non-nullité (NOT NULL)

64
Définition des données

Une contrainte NOT NULL indique simplement qu'une colonne ne peut pas prendre la valeur NULL.
Par exemple :

CREATE TABLE produits (


no_produit integer NOT NULL,
nom text NOT NULL,
prix numeric
);

Une contrainte NOT NULL est toujours écrite comme une contrainte de colonne. Elle est
fonctionnellement équivalente à la création d'une contrainte de vérification CHECK (nom_colonne
IS NOT NULL). Toutefois, dans PostgreSQL, il est plus efficace de créer explicitement une
contrainte NOT NULL. L'inconvénient est que les contraintes de non-nullité ainsi créées ne peuvent
pas être explicitement nommées.

Une colonne peut évidemment avoir plusieurs contraintes. Il suffit d'écrire les contraintes les unes
après les autres :

CREATE TABLE produits (


no_produit integer NOT NULL,
nom text NOT NULL,
prix numeric NOT NULL CHECK (prix > 0)
);

L'ordre n'a aucune importance. Il ne détermine pas l'ordre de vérification des contraintes.

La contrainte NOT NULL a un contraire ; la contrainte NULL. Elle ne signifie pas que la colonne
doit être NULL, ce qui est assurément inutile, mais sélectionne le comportement par défaut, à savoir
que la colonne peut être NULL. La contrainte NULL n'est pas présente dans le standard SQL et ne
doit pas être utilisée dans des applications portables (elle n'a été ajoutée dans PostgreSQL que pour
assurer la compatibilité avec d'autres bases de données). Certains utilisateurs l'apprécient néanmoins,
car elle permet de basculer aisément d'une contrainte à l'autre dans un fichier de script. On peut, par
exemple, commencer avec :

CREATE TABLE produits (


no_produit integer NULL,
nom text NULL,
prix numeric NULL
);

puis insérer le mot-clé NOT en fonction des besoins.

Astuce
Dans la plupart des bases de données, il est préférable que la majorité des colonnes soient
marquées NOT NULL.

5.3.3. Contraintes d'unicité


Les contraintes d'unicité garantissent l'unicité des données contenues dans une colonne ou un groupe
de colonnes par rapport à toutes les lignes de la table. La syntaxe est :

CREATE TABLE produits (


no_produit integer UNIQUE,
nom text,
prix numeric
);

65
Définition des données

lorsque la contrainte est écrite comme contrainte de colonne et :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric,
UNIQUE (no_produit)
);

lorsqu'elle est écrite comme contrainte de table.

Pour définir une contrainte unique pour un groupe de colonnes, saisissez-la en tant que contrainte de
table avec les noms des colonnes séparés par des virgules :

CREATE TABLE exemple (


a integer,
b integer,
c integer,
UNIQUE (a, c)
);

Cela précise que la combinaison de valeurs dans les colonnes indiquées est unique sur toute la table.
Sur une colonne prise isolément, ce n'est pas nécessairement le cas (et habituellement cela ne l'est pas).

Une contrainte d'unicité peut être nommée, de la façon habituelle :

CREATE TABLE produits (


no_produit integer CONSTRAINT doit_etre_different UNIQUE,
nom text,
prix numeric
);

Ajouter une contrainte unique va automatiquement créer un index unique B-tree sur la colonne ou le
groupe de colonnes listées dans la contrainte. Une restriction d'unicité couvrant seulement certaines
lignes ne peut pas être écrite comme une contrainte unique, mais il est possible de forcer ce type de
restriction en créant un index partiel unique.

En général, une contrainte d'unicité est violée si plus d'une ligne de la table possède des valeurs
identiques sur toutes les colonnes de la contrainte. En revanche, deux valeurs NULL ne sont jamais
considérées égales. Cela signifie qu'il est possible de stocker des lignes dupliquées contenant une
valeur NULL dans au moins une des colonnes contraintes. Ce comportement est conforme au standard
SQL, mais d'autres bases SQL n'appliquent pas cette règle. Il est donc préférable d'être prudent lors
du développement d'applications portables.

5.3.4. Clés primaires


Une contrainte de type clé primaire indique qu'une colonne, ou un groupe de colonnes, peuvent être
utilisés comme un identifiant unique de ligne pour cette table. Ceci nécessite que les valeurs soient à la
fois uniques et non NULL. Les définitions de table suivantes acceptent de ce fait les mêmes données :

CREATE TABLE produits (


no_produit integer UNIQUE NOT NULL,
nom text,
prix numeric
);

CREATE TABLE produits (


no_produit integer PRIMARY KEY,
nom text,

66
Définition des données

prix numeric
);

Les clés primaires peuvent également contraindre plusieurs colonnes ; la syntaxe est semblable aux
contraintes d'unicité :

CREATE TABLE exemple (


a integer,
b integer,
c integer,
PRIMARY KEY (a, c)
);

Ajouter une clé primaire créera automatiquement un index unique B-tree sur la colonne ou le groupe
de colonnes listés dans la clé primaire, et forcera les colonnes à être marquées NOT NULL.

L'ajout d'une clé primaire créera automatiquement un index B-tree unique sur la colonne ou le groupe
de colonnes utilisé dans la clé primaire.

Une table a, au plus, une clé primaire. (Le nombre de contraintes UNIQUE NOT NULL, qui assurent
pratiquement la même fonction, n'est pas limité, mais une seule peut être identifiée comme clé
primaire.) La théorie des bases de données relationnelles impose que chaque table ait une clé primaire.
Cette règle n'est pas forcée par PostgreSQL, mais il est préférable de la respecter.

Les clés primaires sont utiles pour la documentation et pour les applications clientes. Par exemple, une
application graphique qui permet la modifier des valeurs des lignes a probablement besoin de connaître
la clé primaire d'une table pour être capable d'identifier les lignes de façon unique. Le système de
bases de données utilise une clé primaire de différentes façons. Par exemple, la clé primaire définit les
colonnes cibles par défaut pour les clés étrangères référençant cette table.

5.3.5. Clés étrangères


Une contrainte de clé étrangère stipule que les valeurs d'une colonne (ou d'un groupe de colonnes)
doivent correspondre aux valeurs qui apparaissent dans les lignes d'une autre table. On dit que cela
maintient l'intégrité référentielle entre les deux tables.

Soit la table de produits, déjà utilisée plusieurs fois :

CREATE TABLE produits (


no_produit integer PRIMARY KEY,
nom text,
prix numeric
);

Soit également une table qui stocke les commandes de ces produits. Il est intéressant de s'assurer que
la table des commandes ne contient que des commandes de produits qui existent réellement. Pour
cela, une contrainte de clé étrangère est définie dans la table des commandes qui référence la table
« produits » :

CREATE TABLE commandes (


id_commande integer PRIMARY KEY,
no_produit integer REFERENCES produits (no_produit),
quantite integer
);

Il est désormais impossible de créer des commandes pour lesquelles les valeurs non NULL de
no_produit n'apparaissent pas dans la table « produits ».

Dans cette situation, on dit que la table des commandes est la table qui référence et la table des
produits est la table référencée. De la même façon, il y a des colonnes qui référencent et des colonnes
référencées.

67
Définition des données

La commande précédente peut être raccourcie en

CREATE TABLE commandes (


id_commande integer PRIMARY KEY,
no_produit integer REFERENCES produits,
quantite integer
);

parce qu'en l'absence de liste de colonnes, la clé primaire de la table de référence est utilisée comme
colonne de référence.

Une contrainte de clé étrangère peut être nommée de la façon habituelle.

Une clé étrangère peut aussi contraindre et référencer un groupe de colonnes. Comme cela a déjà été
évoqué, il faut alors l'écrire sous forme d'une contrainte de table. Exemple de syntaxe :

CREATE TABLE t1 (
a integer PRIMARY KEY,
b integer,
c integer,
FOREIGN KEY (b, c) REFERENCES autre_table (c1, c2)
);

Le nombre et le type des colonnes contraintes doivent correspondre au nombre et au type des colonnes
référencées.

Parfois, il est utile que l'« autre table » d'une clé étrangère soit la même table ; elle est alors appelée
une clé étrangère auto-référencée. Par exemple, si vous voulez que les lignes d'une table représentent
les nœuds d'une structure en arbre, vous pouvez écrire

CREATE TABLE tree (


node_id integer PRIMARY KEY,
parent_id integer REFERENCES tree,
name text,
...
);

Un nœud racine aura la colonne parent_id à NULL, et les enregistrements non NULL de
parent_id seront contraints de référencer des enregistrements valides de la table.

Une table peut contenir plusieurs contraintes de clé étrangère. Les relations n-n entre tables sont
implantées ainsi. Soit des tables qui contiennent des produits et des commandes, avec la possibilité
d'autoriser une commande à contenir plusieurs produits (ce que la structure ci-dessus ne permet pas).
On peut pour cela utiliser la structure de table suivante :

CREATE TABLE produits (


no_produit integer PRIMARY KEY,
nom text,
prix numeric
);

CREATE TABLE commandes (


id_commande integer PRIMARY KEY,
adresse_de_livraison text,
...
);

CREATE TABLE commande_produits (


no_produit integer REFERENCES produits,

68
Définition des données

id_commande integer REFERENCES commandes,


quantite integer,
PRIMARY KEY (no_produit, id_commande)
);

La clé primaire de la dernière table recouvre les clés étrangères.

Les clés étrangères interdisent désormais la création de commandes qui ne sont pas liées à un produit.
Qu'arrive-t-il si un produit est supprimé alors qu'une commande y fait référence ? SQL permet aussi
de le gérer. Intuitivement, plusieurs options existent :

• interdire d'effacer un produit référencé ;


• effacer aussi les commandes ;
• autre chose ?

Pour illustrer ce cas, la politique suivante est implantée sur l'exemple de relations n-n évoqué plus haut :

• quand quelqu'un veut retirer un produit qui est encore référencé par une commande (au travers de
commande_produits), on l'interdit ;
• si quelqu'un supprime une commande, les éléments de la commande sont aussi supprimés.

CREATE TABLE produits (


no_produit integer PRIMARY KEY,
nom text,
prix numeric
);

CREATE TABLE commandes (


id_commande integer PRIMARY KEY,
adresse_de_livraison text,
...
);

CREATE TABLE commande_produits (


no_produit integer REFERENCES produits ON DELETE RESTRICT,
id_commande integer REFERENCES commandes ON DELETE CASCADE,
quantite integer,
PRIMARY KEY (no_produit, id_commande)
);

Restreindre les suppressions et les réaliser en cascade sont les deux options les plus communes.
RESTRICT empêche la suppression d'une ligne référencée. NO ACTION impose la levée d'une erreur
si des lignes référençant existent lors de la vérification de la contrainte. Il s'agit du comportement par
défaut en l'absence de précision. La différence entre RESTRICT et NO ACTION est l'autorisation par
NO ACTION du report de la vérification à la fin de la transaction, ce que RESTRICT ne permet pas.
CASCADE indique que, lors de la suppression d'une ligne référencée, les lignes la référençant doivent
être automatiquement supprimées. Il existe deux autres options : SET NULL et SET DEFAULT.
Celles-ci imposent que les colonnes qui référencent dans les lignes référencées soient réinitialisées
à NULL ou à leur valeur par défaut, respectivement, lors de la suppression d'une ligne référencée.
Elles ne dispensent pas pour autant d'observer les contraintes. Par exemple, si une action précise SET
DEFAULT, mais que la valeur par défaut ne satisfait pas la clé étrangère, l'opération échoue.

À l'instar de ON DELETE existe ON UPDATE, évoqué lorsqu'une colonne référencée est modifiée
(actualisée). Les actions possibles sont les mêmes. Dans ce cas, CASCADE signifie que les valeurs
mises à jour dans la colonne référencée doivent être copiées dans les lignes de référence.

Habituellement, une ligne de référence n'a pas besoin de satisfaire la clé étrangère si une de ses
colonnes est NULL. Si la clause MATCH FULL est ajoutée à la déclaration de la clé étrangère, une ligne
de référence échappe à la clé étrangère seulement si toutes ses colonnes de référence sont NULL (donc

69
Définition des données

un mélange de valeurs NULL et non NULL échoue forcément sur une contrainte MATCH FULL).
Si vous ne voulez pas que les lignes de référence soient capables d'empêcher la satisfaction de la clé
étrangère, déclarez les colonnes de référence comme NOT NULL.

Une clé étrangère doit référencer les colonnes qui soit sont une clé primaire, soit forment une contrainte
d'unicité. Cela signifie que les colonnes référencées ont toujours un index (celui qui garantit la clé
primaire ou la contrainte unique). Donc les vérifications sur la ligne de référence seront performantes.
Comme la suppression d'une ligne de la table référencée ou la mise à jour d'une colonne référencée
nécessitera un parcours de la table référée pour trouver les lignes correspondant à l'ancienne valeur, il
est souvent intéressant d'indexer les colonnes référencées. Comme cela n'est pas toujours nécessaire
et qu'il y a du choix sur la façon d'indexer, l'ajout d'une contrainte de clé étrangère ne crée pas
automatiquement un index sur les colonnes référencées.

Le Chapitre 6 contient de plus amples informations sur l'actualisation et la suppression de données.


Voir aussi la description de la syntaxe des clés étrangères dans la documentation de référence sur
CREATE TABLE.

Une clé étrangère peut faire référence à des colonnes qui constituent une clé primaire ou forment
une contrainte d'unicité. Si la clé étrangère référence une contrainte d'unicité, des possibilités
supplémentaires sont offertes concernant la correspondance des valeurs NULL. Celles-ci sont
expliquées dans la documentation de référence de CREATE TABLE.

5.3.6. Contraintes d'exclusion


Les contraintes d'exclusion vous assurent que si deux lignes sont comparées sur les colonnes
ou expressions spécifiées en utilisant les opérateurs indiqués, au moins une de ces comparaisons
d'opérateurs renverra false ou NULL. La syntaxe est :

CREATE TABLE cercles (


c circle,
EXCLUDE USING gist (c WITH &&)
);

Voir aussi CREATE TABLE ... CONSTRAINT ... EXCLUDE pour plus de détails.

L'ajout d'une contrainte d'exclusion créera automatiquement un index du type spécifié dans la
déclaration de la contrainte.

5.4. Colonnes système


Chaque table contient plusieurs colonnes système implicitement définies par le système. De ce fait,
leurs noms ne peuvent pas être utilisés comme noms de colonnes utilisateur (ces restrictions sont
distinctes de celles sur l'utilisation de mot-clés ; mettre le nom entre guillemets ne permet pas
d'échapper à cette règle). Il n'est pas vraiment utile de se préoccuper de ces colonnes, mais au minimum
de savoir qu'elles existent.

oid

L'identifiant objet (object ID) d'une ligne. Cette colonne n'est présente que si la table a été créée
en précisant WITH OIDS ou si la variable de configuration default_with_oids était activée à ce
moment-là. Cette colonne est de type oid (même nom que la colonne) ; voir la Section 8.19 pour
obtenir plus d'informations sur ce type.

tableoid

L' OID de la table contenant la ligne. Cette colonne est particulièrement utile pour les requêtes
qui utilisent des hiérarchies d'héritage (voir Section 5.9). Il est, en effet, difficile, en son absence,

70
Définition des données

de savoir de quelle table provient une ligne. tableoid peut être joint à la colonne oid de
pg_class pour obtenir le nom de la table.

xmin

L'identifiant (ID de transaction) de la transaction qui a inséré cette version de la ligne. (Une version
de ligne est un état individuel de la ligne ; toute mise à jour d'une ligne crée une nouvelle version
de ligne pour la même ligne logique.)

cmin

L'identifiant de commande (à partir de zéro) au sein de la transaction d'insertion.

xmax

L'identifiant (ID de transaction) de la transaction de suppression, ou zéro pour une version de ligne
non effacée. Il est possible que la colonne ne soit pas nulle pour une version de ligne visible ; cela
indique habituellement que la transaction de suppression n'a pas été effectuée, ou qu'une tentative
de suppression a été annulée.

cmax

L'identifiant de commande au sein de la transaction de suppression, ou zéro.

ctid

La localisation physique de la version de ligne au sein de sa table. Bien que le ctid puisse être
utilisé pour trouver la version de ligne très rapidement, le ctid d'une ligne change si la ligne est
actualisée ou déplacée par un VACUUM FULL. ctid est donc inutilisable comme identifiant de
ligne sur le long terme. Il est préférable d'utiliser l'OID, ou, mieux encore, un numéro de série
utilisateur, pour identifier les lignes logiques.

Les OID sont des nombres de 32 bits et sont attribués à partir d'un compteur unique sur le cluster. Dans
une base de données volumineuse ou âgée, il est possible que le compteur boucle. Il est de ce fait peu
pertinent de considérer que les OID puissent être uniques ; pour identifier les lignes d'une table, il est
fortement recommandé d'utiliser un générateur de séquence. Néanmoins, les OID peuvent également
être utilisés sous réserve que quelques précautions soient prises :

• une contrainte d'unicité doit être ajoutée sur la colonne OID de chaque table dont l'OID est utilisé
pour identifier les lignes. Dans ce cas (ou dans celui d'un index d'unicité), le système n'engendre
pas d'OID qui puisse correspondre à celui d'une ligne déjà présente. Cela n'est évidemment possible
que si la table contient moins de 232 (4 milliards) lignes ; en pratique, la taille de la table a tout
intérêt à être bien plus petite que ça, dans un souci de performance ;

• l'unicité intertables des OID ne doit jamais être envisagée ; pour obtenir un identifiant unique sur
l'ensemble de la base, il faut utiliser la combinaison du tableoid et de l'OID de ligne ;

• les tables en question doivent être créées avec l'option WITH OIDS. Depuis PostgreSQL 8.1,
WITHOUT OIDS est l'option par défaut.

Les identifiants de transaction sont aussi des nombres de 32 bits. Dans une base de données âgée, il
est possible que les ID de transaction bouclent. Cela n'est pas un problème fatal avec des procédures
de maintenance appropriées ; voir le Chapitre 24 pour les détails. Il est, en revanche, imprudent de
considérer l'unicité des ID de transaction sur le long terme (plus d'un milliard de transactions).

Les identifiants de commande sont aussi des nombres de 32 bits. Cela crée une limite dure de 232
(4 milliards) commandes SQL au sein d'une unique transaction. En pratique, cette limite n'est pas un
problème -- la limite est sur le nombre de commandes SQL, pas sur le nombre de lignes traitées. De
plus, seules les commandes qui modifient réellement le contenu de la base de données consomment
un identifiant de commande.

5.5. Modification des tables


71
Définition des données

Lorsqu'une table est créée et qu'une erreur a été commise ou que les besoins de l'application changent,
il est alors possible de la supprimer et de la récréer. Cela n'est toutefois pas pratique si la table contient
déjà des données ou qu'elle est référencée par d'autres objets de la base de données (une contrainte de
clé étrangère, par exemple). C'est pourquoi PostgreSQL offre une série de commandes permettant de
modifier une table existante. Cela n'a rien à voir avec la modification des données contenues dans la
table ; il ne s'agit ici, que de modifier la définition, ou structure, de la table.

Il est possible

• d'ajouter des colonnes ;


• de supprimer des colonnes ;
• d'ajouter des contraintes ;
• de supprimer des contraintes ;
• de modifier des valeurs par défaut ;
• de modifier les types de données des colonnes ;
• de renommer des colonnes ;
• de renommer des tables.

Toutes ces actions sont réalisées à l'aide de la commande ALTER TABLE, dont la page de référence
est bien plus détaillée.

5.5.1. Ajouter une colonne


La commande d'ajout d'une colonne ressemble à :

ALTER TABLE produits ADD COLUMN description text;

La nouvelle colonne est initialement remplie avec la valeur par défaut précisée (NULL en l'absence
de clause DEFAULT).

Astuce
À partir de PostgreSQL 11, ajouter une colonne avec une valeur par défaut constante ne signifie
plus que chaque ligne de la table doit être mise à jour quand l'instruction ALTER TABLE
doit être exécutée. À la place, la valeur par défaut sera renvoyée à chaque accès à la ligne et
appliquée quand la table est réécrite, rendant ainsi la commande ALTER TABLE bien plus
rapide, même sur de grosses tables.

Néanmoins, si la valeur par défaut est volatile (par exemple clock_timestamp()), chaque
ligne devra être mise à jour avec la valeur calculée à l'exécution du ALTER TABLE. Pour éviter
une opération de mise à jour potentiellement longue, et en particulier si vous avez de toute
façon l'intention de remplir la colonne avec des valeurs qui ne sont pas par défaut, il pourrait
être préférable d'ajouter la colonne sans valeur par défaut, d'insérer les valeurs correctes en
utilisant l'instruction UPDATE, et enfin d'ajouter la valeur par désirée comme décrit ci-dessous.

Des contraintes de colonne peuvent être définies dans la même commande, à l'aide de la syntaxe
habituelle :

ALTER TABLE produits ADD COLUMN description text CHECK (description


<> '');

En fait, toutes les options applicables à la description d'une colonne dans CREATE TABLE
peuvent être utilisées ici. Il ne faut toutefois pas oublier que la valeur par défaut doit satisfaire les
contraintes données. Dans le cas contraire, ADD échoue. Il est aussi possible d'ajouter les contraintes
ultérieurement (voir ci-dessous) après avoir rempli la nouvelle colonne correctement.

5.5.2. Supprimer une colonne

72
Définition des données

La commande de suppression d'une colonne ressemble à celle-ci :

ALTER TABLE produits DROP COLUMN description;

Toute donnée dans cette colonne disparaît. Les contraintes de table impliquant la colonne sont
également supprimées. Néanmoins, si la colonne est référencée par une contrainte de clé étrangère
d'une autre table, PostgreSQL ne supprime pas silencieusement cette contrainte. La suppression de
tout ce qui dépend de la colonne peut être autorisée en ajoutant CASCADE :

ALTER TABLE produits DROP COLUMN description CASCADE;

Voir la Section 5.13 pour une description du mécanisme général.

5.5.3. Ajouter une contrainte


Pour ajouter une contrainte, la syntaxe de contrainte de table est utilisée. Par exemple :

ALTER TABLE produits ADD CHECK (nom <> '');


ALTER TABLE produits ADD CONSTRAINT autre_nom UNIQUE (no_produit);
ALTER TABLE produits ADD FOREIGN KEY (id_groupe_produit) REFERENCES
groupes_produits;

Pour ajouter une contrainte NOT NULL, qui ne peut pas être écrite sous forme d'une contrainte de
table, la syntaxe suivante est utilisée :

ALTER TABLE produits ALTER COLUMN no_produit SET NOT NULL;

La contrainte étant immédiatement vérifiée, les données de la table doivent satisfaire la contrainte
avant qu'elle ne soit ajoutée.

5.5.4. Supprimer une contrainte


Pour supprimer une contrainte, il faut connaître son nom. Si elle a été explicitement nommée, il n'y
a aucune difficulté. Dans le cas contraire, le système a affecté un nom généré qu'il faudra identifier.
La commande \d table de psql peut être utile ici ; d'autres interfaces offrent aussi la possibilité
d'examiner les détails de table. La commande est :

ALTER TABLE produits DROP CONSTRAINT un_nom;

(Dans le cas d'un nom de contrainte généré par le système, comme $2, il est nécessaire de l'entourer
de guillemets doubles (") pour en faire un identifiant valable.)

Comme pour la suppression d'une colonne, CASCADE peut être ajouté pour supprimer une contrainte
dont dépendent d'autres objets. Une contrainte de clé étrangère, par exemple, dépend d'une contrainte
de clé primaire ou d'unicité sur la(les) colonne(s) référencée(s).

Cela fonctionne de la même manière pour tous les types de contraintes, à l'exception des contraintes
NOT NULL. Pour supprimer une contrainte NOT NULL, on écrit :

ALTER TABLE produits ALTER COLUMN no_produit DROP NOT NULL;

(Les contraintes NOT NULL n'ont pas de noms.)

5.5.5. Modifier la valeur par défaut d'une colonne


La commande de définition d'une nouvelle valeur par défaut de colonne ressemble à celle-ci :

ALTER TABLE produits ALTER COLUMN prix SET DEFAULT 7.77;

Cela n'affecte pas les lignes existantes de la table, mais uniquement la valeur par défaut pour les futures
commandes INSERT.

73
Définition des données

Pour retirer toute valeur par défaut, on écrit :

ALTER TABLE produits ALTER COLUMN prix DROP DEFAULT;

C'est équivalent à mettre la valeur par défaut à NULL. En conséquence, il n'y a pas d'erreur à retirer
une valeur par défaut qui n'a pas été définie, car NULL est la valeur par défaut implicite.

5.5.6. Modifier le type de données d'une colonne


La commande de conversion du type de données d'une colonne ressemble à celle-ci :

ALTER TABLE produits ALTER COLUMN prix TYPE numeric(10,2);

Elle ne peut réussir que si chaque valeur de la colonne peut être convertie dans le nouveau type par
une conversion implicite. Si une conversion plus complexe est nécessaire, une clause USING peut être
ajoutée qui indique comment calculer les nouvelles valeurs à partir des anciennes.

PostgreSQL tente de convertir la valeur par défaut de la colonne le cas échéant, ainsi que toute
contrainte impliquant la colonne. Mais ces conversions peuvent échouer ou produire des résultats
surprenants. Il est souvent préférable de supprimer les contraintes de la colonne avant d'en modifier
le type, puis d'ajouter ensuite les contraintes convenablement modifiées.

5.5.7. Renommer une colonne


Pour renommer une colonne :

ALTER TABLE produits RENAME COLUMN no_produit TO numero_produit;

5.5.8. Renommer une table


Pour renommer une table :

ALTER TABLE produits RENAME TO elements;

5.6. Droits
Quand un objet est créé, il se voit affecter un propriétaire. Le propriétaire est normalement le rôle qui
a exécuté la requête de création. Pour la plupart des objets, l'état initial est que seul le propriétaire
(et les superutilisateurs) peut faire quelque chose avec cet objet. Pour permettre aux autres rôles de
l'utiliser, des droits doivent être donnés.

Il existe un certain nombre de droits différents : SELECT, INSERT, UPDATE, DELETE, TRUNCATE,
REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE et USAGE. Les droits
applicables à un objet particulier varient selon le type d'objet (table, fonction...). La page de référence
GRANT fournit une information complète sur les différents types de droits gérés par PostgreSQL. La
section et les chapitres suivants présentent l'utilisation de ces droits.

Le droit de modifier ou de détruire un objet est le privilège du seul propriétaire.

Un objet peut se voir affecter un nouveau propriétaire avec la commande ALTER correspondant
à l'objet, par exemple ALTER TABLE. Les superutilisateurs peuvent toujours le faire. Les rôles
ordinaires peuvent seulement le faire s'ils sont le propriétaire actuel de l'objet (ou un membre du rôle
propriétaire) et un membre du nouveau rôle propriétaire.

La commande GRANT est utilisée pour accorder des privilèges. Par exemple, si joe est un rôle et
comptes une table, le privilège d'actualiser la table comptes peut être accordé à joe avec :

GRANT UPDATE ON comptes TO joe;

74
Définition des données

Écrire ALL à la place d'un droit spécifique accorde tous les droits applicables à ce type d'objet.

Le nom de « rôle » spécial PUBLIC peut être utilisé pour donner un privilège à tous les rôles du
système. De plus, les rôles de type « group » peuvent être configurés pour aider à la gestion des droits
quand il y a beaucoup d'utilisateurs dans une base -- pour les détails, voir Chapitre 21.

Pour révoquer un privilège, on utilise la commande bien nommée REVOKE, comme dans l'exemple
ci-dessous :

REVOKE ALL ON comptes FROM PUBLIC;

Les privilèges spéciaux du propriétaire de l'objet (c'est-à-dire le droit d'exécuter DROP, GRANT,
REVOKE, etc.) appartiennent toujours implicitement au propriétaire. Ils ne peuvent être ni accordés ni
révoqués. Mais le propriétaire de l'objet peut choisir de révoquer ses propres droits ordinaires pour,
par exemple, mettre une table en lecture seule pour lui-même et pour les autres.

Habituellement, seul le propriétaire de l'objet (ou un superutilisateur) peut accorder ou révoquer


les droits sur un objet. Néanmoins, il est possible de donner un privilège « avec possibilité de
transmission » (« with grant option »), qui donne à celui qui le reçoit la permission de le donner à
d'autres. Si cette option est ensuite révoquée, alors tous ceux qui ont reçu ce privilège par cet utilisateur
(directement ou indirectement via la chaîne des dons) perdent ce privilège. Pour les détails, voir les
pages de références GRANT et REVOKE.

5.7. Politiques de sécurité niveau ligne


En plus des systèmes de droits du standard SQL disponibles via GRANT, les tables peuvent avoir des
politiques de sécurité pour l'accès aux lignes qui restreignent, utilisateur par utilisateur, les lignes qui
peuvent être renvoyées par les requêtes d'extraction ou les commandes d'insertions, de mises à jour ou
de suppressions. Cette fonctionnalité est aussi connue sous le nom Row-Level Security. Par défaut, les
tables n'ont aucune politique de ce type pour que, si un utilisateur a accès à une table selon les droits
du standard SQL, toutes les lignes de la table soient accessibles aux requêtes de lecture ou d'écriture.

Lorsque la protection des lignes est activée sur une table (avec l'instruction ALTER TABLE ...
ENABLE ROW LEVEL SECURITY), tous les accès classiques à la table pour sélectionner ou
modifier des lignes doivent être autorisés par une politique de sécurité. Cependant, le propriétaire de
la table n'est typiquement pas soumis aux politiques de sécurité. Si aucune politique n'existe pour la
table, une politique de rejet est utilisée par défaut, ce qui signifie qu'aucune ligne n'est visible ou ne
peut être modifiée. Les opérations qui s'appliquent pour la table dans sa globalité, comme TRUNCATE
et REFERENCES, ne sont pas soumises à ces restrictions de niveau ligne.

Les politiques de sécurité niveau ligne peuvent s'appliquer en particulier soit à des commandes, soit à
des rôles, soit aux deux. Une politique est indiquée comme s'appliquant à toutes les commandes par
ALL, ou seulement à SELECT, INSERT, UPDATE ou DELETE. Plusieurs rôles peuvent être affectés
à une politique donnée, et les règles normales d'appartenance et d'héritage s'appliquent.

Pour indiquer les lignes visibles ou modifiables pour une politique, une expression renvoyant un
booléen est requise. Cette expression sera évaluée pour chaque ligne avant toutes conditions ou
fonctions qui seraient indiquées dans les requêtes de l'utilisateur. (La seule exception à cette règle
est les fonctions marquées leakproof, qui annoncent ne pas dévoiler d'information ; l'optimiseur
peut choisir d'appliquer de telles fonctions avant les vérifications de sécurité niveau ligne). Les lignes
pour lesquelles l'expression ne renvoie pas true ne sont pas traitées. Des expressions différentes
peuvent être indiquées pour fournir des contrôles indépendants pour les lignes qui sont visibles et pour
celles qui sont modifiées. Les expressions attachées à la politique sont exécutées dans le cours de la
requête et avec les droits de l'utilisateur qui exécute la commande, bien que les fonctions définies avec
l'attribut SECURITY DEFINER puissent être utilisées pour accéder à des données qui ne seraient pas
disponibles à l'utilisateur effectuant la requête.

Les superutilisateurs et les rôles avec l'attribut BYPASSRLS ne sont pas soumis au système de sécurité
niveau ligne lorsqu'ils accèdent une table. Il en est de même par défaut du propriétaire d'une table, bien

75
Définition des données

qu'il puisse choisir de se soumettre à ces contrôles avec ALTER TABLE ... FORCE ROW LEVEL
SECURITY.

L'activation ou la désactivation de la sécurité niveau ligne, comme l'ajout des polices à une table, est
toujours le privilège du seul propriétaire de la table.

Les politiques sont créées en utilisant l'instruction CREATE POLICY, modifiées avec la commande
ALTER POLICY et supprimées avec la commande DROP POLICY. Pour activer et désactiver la
sécurité niveau ligne pour une table donnée, utilisez la commande ALTER TABLE.

Chaque politique possède un nom et de multiples politiques peuvent être définies pour une table.
Comme les politiques sont spécifiques à une table, chaque politique pour une même table doit avoir
un nom différent. Différentes tables peuvent avoir des noms de politique de même nom.

Lorsque plusieurs politiques sont applicables pour une même requête, elles sont combinées en utilisant
OR (pour les politiques permissives, ce qui est le comportement par défaut) ou en utilisant AND (pour
les politiques restrictives). C'est similaire à la règle qu'un rôle donné a les privilèges de tous les rôles
dont il est membre. Les politiques permissives et restrictives sont discutées plus en détail ci-dessous.

À titre de simple exemple, nous allons ici créer une politique sur la relation comptes pour autoriser
seulement les membres du rôle admins à accéder seulement aux lignes de leurs propres comptes :

CREATE TABLE comptes (admin text, societe text, contact_email


text);

ALTER TABLE comptes ENABLE ROW LEVEL SECURITY;

CREATE POLICY compte_admins ON comptes TO admins


USING (admin = current_user);

La politique ci-dessus prévoit implicitement une clause WITH CHECK identique à sa clause USING, de
sorte que la contrainte s'applique à la fois aux lignes sélectionnées par une commande (un gestionnaire
ne peut donc pas utiliser SELECT, UPDATE, ou DELETE sur des lignes existantes appartenant à
un gestionnaire différent) et aux lignes modifiées par une commande (les lignes appartenant à un
gestionnaire différent ne peuvent donc être créées avec INSERT ou UPDATE).

Si aucun rôle n'est indiqué ou si le nom de pseudo rôle PUBLIC est utilisé, alors la politique s'applique
à tous les utilisateurs du système. Pour autoriser tous les utilisateurs à accéder à leurs propres lignes
dans une table utilisateurs, une simple politique peut être utilisée :

CREATE POLICY police_utilisateur ON utilisateurs


USING (user_name = current_user);

Cela fonctionne de la même manière que dans l'exemple précédent.

Pour utiliser une politique différente pour les lignes ajoutées à la table de celle appliquée pour les
lignes visibles, plusieurs politiques peuvent être combinées. Cette paire de politiques autorisera tous
les utilisateurs à voir toutes les lignes de la table utilisateurs, mais seulement à modifier les
leurs :

CREATE POLICY user_sel_policy ON users


FOR SELECT
USING (true);
CREATE POLICY user_mod_policy ON users
USING (user_name = current_user);

76
Définition des données

Pour une commande SELECT, ces deux politique sont combinées à l'aide de OR, ayant pour effet que
toutes les lignes peuvent être sélectionnées. Pour les autres types de commandes, seule la deuxième
politique s'applique, de sorte que les effets sont les mêmes qu'auparavant.

La sécurité niveau ligne peut également être désactivée avec la commande ALTER TABLE. La
désactivation de la sécurité niveau ligne ne supprime pas les polices qui sont définies pour la table ;
elles sont simplement ignorées. L'ensemble des lignes sont alors visibles et modifiables, selon le
système standard des droits SQL.

Ci-dessous se trouve un exemple plus important de la manière dont cette fonctionnalité peut être
utilisée en production. La table passwd simule le fichier des mots de passe d'un système Unix.

-- Simple exemple basé sur le fichier passwd


CREATE TABLE passwd (
user_name text UNIQUE NOT NULL,
pwhash text,
uid int PRIMARY KEY,
gid int NOT NULL,
real_name text NOT NULL,
home_phone text,
extra_info text,
home_dir text NOT NULL,
shell text NOT NULL
);

CREATE ROLE admin; -- Administrateur


CREATE ROLE bob; -- Utilisateur normal
CREATE ROLE alice; -- Utilisateur normal

-- Chargement de la table
INSERT INTO passwd VALUES
('admin','xxx',0,0,'Admin','111-222-3333',null,'/root','/bin/
dash');
INSERT INTO passwd VALUES
('bob','xxx',1,1,'Bob','123-456-7890',null,'/home/bob','/bin/
zsh');
INSERT INTO passwd VALUES
('alice','xxx',2,1,'Alice','098-765-4321',null,'/home/alice','/
bin/zsh');

-- Assurez-vous d'activer le row level security pour la table


ALTER TABLE passwd ENABLE ROW LEVEL SECURITY;

-- Créer les polices


-- L'administrateur peut voir toutes les lignes et en ajouter comme
il le souhaite
CREATE POLICY admin_all ON passwd TO admin USING (true) WITH CHECK
(true);
-- Les utilisateurs normaux peuvent voir toutes les lignes
CREATE POLICY all_view ON passwd FOR SELECT USING (true);
-- Les utilisateurs normaux peuvent mettre à jour leurs propres
lignes,
-- tout en limitant les shells qu'ils peuvent choisir
CREATE POLICY user_mod ON passwd FOR UPDATE
USING (current_user = user_name)
WITH CHECK (
current_user = user_name AND

77
Définition des données

shell IN ('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/
tcsh')
);

-- Donner à admin tous les droits normaux


GRANT SELECT, INSERT, UPDATE, DELETE ON passwd TO admin;
-- Les utilisateurs ne peuvent que sélectionner des colonnes
publiques
GRANT SELECT
(user_name, uid, gid, real_name, home_phone, extra_info,
home_dir, shell)
ON passwd TO public;
-- Autoriser les utilisateurs à mettre à jour certaines colonnes
GRANT UPDATE
(pwhash, real_name, home_phone, extra_info, shell)
ON passwd TO public;

Comme avec tous les réglages de sécurité, il est important de tester et de s'assurer que le système se
comporte comme attendu. En utilisant l'exemple ci-dessus, les manipulations ci-dessous montrent que
le système des droits fonctionne correctement.

-- admin peut voir toutes les lignes et les colonnes


postgres=> set role admin;
SET
postgres=> table passwd;
user_name | pwhash | uid | gid | real_name | home_phone |
extra_info | home_dir | shell
-----------+--------+-----+-----+-----------+--------------
+------------+-------------+-----------
admin | xxx | 0 | 0 | Admin | 111-222-3333 |
| /root | /bin/dash
bob | xxx | 1 | 1 | Bob | 123-456-7890 |
| /home/bob | /bin/zsh
alice | xxx | 2 | 1 | Alice | 098-765-4321 |
| /home/alice | /bin/zsh
(3 rows)

-- Tester ce que Alice est capable de faire:


postgres=> set role alice;
SET
postgres=> table passwd;
ERROR: permission denied for table passwd
postgres=> select
user_name,real_name,home_phone,extra_info,home_dir,shell from
passwd;
user_name | real_name | home_phone | extra_info | home_dir |
shell
-----------+-----------+--------------+------------+-------------
+-----------
admin | Admin | 111-222-3333 | | /root
| /bin/dash
bob | Bob | 123-456-7890 | | /home/bob
| /bin/zsh
alice | Alice | 098-765-4321 | | /home/alice
| /bin/zsh
(3 rows)

78
Définition des données

postgres=> update passwd set user_name = 'joe';


ERROR: permission denied for table passwd
-- Alice est autorisée à modifier son propre nom (real_name), mais
pas celui des autres
postgres=> update passwd set real_name = 'Alice Doe';
UPDATE 1
postgres=> update passwd set real_name = 'John Doe' where user_name
= 'admin';
UPDATE 0
postgres=> update passwd set shell = '/bin/xx';
ERROR: new row violates WITH CHECK OPTION for "passwd"
postgres=> delete from passwd;
ERROR: permission denied for table passwd
postgres=> insert into passwd (user_name) values ('xxx');
ERROR: permission denied for table passwd
-- Alice peut modifier son propre mot de passe; RLS empêche
silencieusement la mise à jour d'autres lignes
postgres=> update passwd set pwhash = 'abc';
UPDATE 1

Toutes les politiques construites jusqu'à maintenant étaient des politiques permissives, ce qui veut dire
que quand plusieurs politiques sont appliquées, elles sont combinées en utilisant l'opérateur booléen
« OR ». Bien que les politiques permissives puissent être construites pour autoriser l'accès à des
lignes dans les cas attendus, il peut être plus simple de combiner des politiques permissives avec des
politiques restrictives (que l'enregistrement doit passer et qui sont combinées en utilisant l'opérateur
booléen « AND »). En continuant sur l'exemple ci-dessus, nous ajoutons une politique restrictive pour
exiger que l'administrateur soit connecté via un socket unix locale pour accéder aux enregistrements
de la table passwd :

CREATE POLICY admin_local_only ON passwd AS RESTRICTIVE TO admin


USING (pg_catalog.inet_client_addr() IS NULL);

Nous pouvons alors voir qu'un administrateur se connectant depuis le réseau ne verra aucun
enregistrement, du fait de la politique restrictive :

=> SELECT current_user;


current_user
--------------
admin
(1 row)

=> select inet_client_addr();


inet_client_addr
------------------
127.0.0.1
(1 row)

=> SELECT current_user;


current_user
--------------
admin
(1 row)

=> TABLE passwd;

79
Définition des données

user_name | pwhash | uid | gid | real_name | home_phone |


extra_info | home_dir | shell
-----------+--------+-----+-----+-----------+------------
+------------+----------+-------
(0 rows)

=> UPDATE passwd set pwhash = NULL;


UPDATE 0

Les vérifications d'intégrité référentielle, telles que les contraintes d'unicité ou de clefs primaires et
les références de clefs étrangères, passent toujours outre la sécurité niveau ligne pour s'assurer que
l'intégrité des données est maintenue. Une attention particulière doit être prise lors de la mise en place
des schémas et des politiques de sécurité de niveau ligne pour éviter qu'un canal caché (covert
channel) ne dévoile des informations à travers de telles vérifications d'intégrité référentielle.

Dans certains contextes, il est important de s'assurer que la sécurité niveau ligne n'est pas appliquée. Par
exemple, lors d'une sauvegarde, il serait désastreux si la sécurité niveau ligne avait pour conséquence
de soustraire silencieusement certaines lignes de la sauvegarde. Dans une telle situation, vous
pouvez positionner le paramètre de configuration row_security à off. En lui-même, ce paramètre ne
contourne pas la sécurité niveau ligne ; ce qu'il fait, c'est qu'il lève une erreur si le résultat d'une des
requêtes venait à être filtrée par une politique. La raison de l'erreur peut alors être recherchée et résolue.

Dans les exemples ci-dessus, les expressions attachées aux politiques considèrent uniquement les
valeurs de la ligne courante accédée ou modifiée. C'est le cas le plus simple et le plus performant ;
lorsque c'est possible, il est préférable de concevoir les applications qui utilisent cette fonctionnalité
de la sorte. S'il est nécessaire de consulter d'autres lignes ou tables pour que la politique puisse prendre
une décision, ceci peut être réalisé en utilisant dans les expressions des politiques des sous-requêtes
SELECT ou des fonctions qui contiennent des commandes SELECT. Cependant, faites attention que
de tels accès ne créent pas des accès concurrents qui pourraient permettre une fuite d'informations si
aucune précaution n'est prise. À titre d'exemple, considérez la création de la table suivante :

-- définition des droits de groupes


CREATE TABLE groupes (groupe_id int PRIMARY KEY,
nom_groupe text NOT NULL);

INSERT INTO groupes VALUES


(1, 'bas'),
(2, 'moyen'),
(5, 'haut');

GRANT ALL ON groupes TO alice; -- alice est l'administratrice


GRANT SELECT ON groupes TO public;

-- définition des niveaux de droits utilisateurs


CREATE TABLE utilisateurs (nom_utilisateur text PRIMARY KEY,
groupe_id int NOT NULL REFERENCES groupes);

INSERT INTO utilisateurs VALUES


('alice', 5),
('bob', 2),
('mallory', 2);

GRANT ALL ON utilisateurs TO alice;


GRANT SELECT ON utilisateurs TO public;

-- table contenant l'information à protéger


CREATE TABLE information (info text,

80
Définition des données

groupe_id int NOT NULL REFERENCES


groupes);

INSERT INTO information VALUES


('peu secret', 1),
('légèrement secret', 2),
('très secret', 5);

ALTER TABLE information ENABLE ROW LEVEL SECURITY;

-- une ligne devrait être visible et modifiable pour les


utilisateurs
-- dont le groupe_id est égal ou plus grand au groupe_id de la
ligne
CREATE POLICY fp_s ON information FOR SELECT
USING (groupe_id <= (SELECT groupe_id FROM utilisateurs WHERE
nom_utilisateur = current_user));
CREATE POLICY fp_u ON information FOR UPDATE
USING (groupe_id <= (SELECT groupe_id FROM utilisateurs WHERE
nom_utilisateur = current_user));

-- nous comptons sur les RLS pour protéger la table information


GRANT ALL ON information TO public;

Maintenant, supposez qu'alice souhaite modifier l'information « légèrement secrète », mais décide
que mallory ne devrait pas pouvoir obtenir ce nouveau contenu, elle le fait ainsi :

BEGIN;
UPDATE utilisateurs SET groupe_id = 1 WHERE nom_utilisateur =
'mallory';
UPDATE information SET info = 'caché à mallory' WHERE groupe_id =
2;
COMMIT;

Ceci semble correct, il n'y a pas de fenêtre pendant laquelle mallory devrait pouvoir accéder à la
chaîne « caché à mallory ». Cependant, il y a une situation de compétition ici. Si mallory fait en
parallèle, disons :

SELECT * FROM information WHERE groupe_id = 2 FOR UPDATE;

et sa transaction est en mode READ COMMITED, il est possible qu'elle voie « caché à mallory ».
C'est possible si sa transaction accède la ligne information juste après qu'alice l'ait fait. Elle
est bloquée en attendant que la transaction d'alice valide, puis récupère la ligne mise à jour grâce
à la clause FOR UPDATE. Cependant, elle ne récupère pas une ligne mise à jour pour la commande
implicite SELECT sur la table utilisateurs parce que cette sous-commande n'a pas la clause FOR
UPDATE ; à la place, la ligne utilisateurs est lue avec une image de la base de données prise
au début de la requête. Ainsi, l'expression de la politique teste l'ancienne valeur du niveau de droit de
mallory et l'autorise à voir la valeur mise à jour.

Il y a plusieurs solutions à ce problème. Une simple réponse est d'utiliser SELECT ... FOR SHARE
dans la sous-commande SELECT de la politique de sécurité niveau ligne. Cependant, ceci demande
de donner le droit UPDATE sur la table référencée (ici utilisateurs) aux utilisateurs concernés,
ce qui peut ne pas être souhaité. (Une autre politique de sécurité niveau ligne pourrait être mise en
place pour les empêcher d'exercer ce droit ; ou la sous-commande SELECT pourrait être incluse

81
Définition des données

dans une fonction marquée security definer). De plus, l'utilisation intensive concurrente de
verrous partagés sur les lignes de la table référencée pourrait poser un problème de performance, tout
spécialement si des mises à jour de cette table sont fréquentes. Une autre solution envisageable, si les
mises à jour de la table référencée ne sont pas fréquentes, est de prendre un verrou de type ACCESS
EXCLUSIVE sur la table référencée lors des mises à jour, de telle manière qu'aucune autre transaction
concurrente ne pourrait consulter d'anciennes valeurs. Ou une transaction pourrait attendre que toutes
les transactions se terminent après avoir validé une mise à jour de la table référencée et avant de faire
des modifications qui reposent sur la nouvelle politique de sécurité.

Pour plus de détails, voir CREATE POLICY et ALTER TABLE.

5.8. Schémas
Une instance de bases de données PostgreSQL contient une ou plusieurs base(s) nommée(s). Les rôles
et quelques autres types d'objets sont partagés sur l'ensemble de l'instance. Une connexion cliente au
serveur ne peut accéder qu'aux données d'une seule base, celle indiquée dans la requête de connexion.

Note
Les rôles d'une instance n'ont pas obligatoirement le droit d'accéder à toutes les bases de
l'instance. Le partage des noms de rôles signifie qu'il ne peut pas y avoir plusieurs rôles
nommés joe, par exemple, dans deux bases de la même instance ; mais le système peut être
configuré pour n'autoriser joe à accéder qu'à certaines bases.

Une base de données contient un (ou plusieurs) schéma(s) nommé(s) qui, eux, contiennent des
tables. Les schémas contiennent aussi d'autres types d'objets nommés (types de données, fonctions
et opérateurs, par exemple). Le même nom d'objet peut être utilisé dans différents schémas sans
conflit ; par exemple, schema1 et mon_schema peuvent tous les deux contenir une table nommée
ma_table. À la différence des bases de données, les schémas ne sont pas séparés de manière rigide :
un utilisateur peut accéder aux objets de n'importe quel schéma de la base de données à laquelle il est
connecté, sous réserve qu'il en ait le droit.

Il existe plusieurs raisons d'utiliser les schémas :

• autoriser de nombreux utilisateurs à utiliser une base de données sans interférer avec les autres ;

• organiser les objets de la base de données en groupes logiques afin de faciliter leur gestion ;

• les applications tierces peuvent être placées dans des schémas séparés pour éviter les collisions avec
les noms d'autres objets.

Les schémas sont comparables aux répertoires du système d'exploitation, à ceci près qu'ils ne peuvent
pas être imbriqués.

5.8.1. Créer un schéma


Pour créer un schéma, on utilise la commande CREATE SCHEMA. Le nom du schéma est libre. Par
exemple :

CREATE SCHEMA mon_schema;

Pour créer les objets d'un schéma ou y accéder, on écrit un nom qualifié constitué du nom du schéma
et du nom de la table séparés par un point :

schema.table

Cela fonctionne partout où un nom de table est attendu, ce qui inclut les commandes de modification
de la table et les commandes d'accès aux données discutées dans les chapitres suivants. (Pour des

82
Définition des données

raisons de simplification, seules les tables sont évoquées, mais les mêmes principes s'appliquent aux
autres objets nommés, comme les types et les fonctions.)

La syntaxe encore plus générale

base.schema.table

peut aussi être utilisée, mais à l'heure actuelle, cette syntaxe n'existe que pour des raisons de conformité
avec le standard SQL. Si un nom de base de données est précisé, ce doit être celui de la base à laquelle
l'utilisateur est connecté.

Pour créer une table dans le nouveau schéma, on utilise :

CREATE TABLE mon_schema.ma_table (


...
);

Pour effacer un schéma vide (tous les objets qu'il contient ont été supprimés), on utilise :

DROP SCHEMA mon_schema;

Pour effacer un schéma et les objets qu'il contient, on utilise :

DROP SCHEMA mon_schema CASCADE;

La Section 5.13 décrit le mécanisme général sous-jacent.

Il n'est pas rare de vouloir créer un schéma dont un autre utilisateur est propriétaire (puisque c'est l'une
des méthodes de restriction de l'activité des utilisateurs à des namespaces prédéfinis). La syntaxe en
est :

CREATE SCHEMA nom_schema AUTHORIZATION nom_utilisateur;

Le nom du schéma peut être omis, auquel cas le nom de l'utilisateur est utilisé. Voir la Section 5.8.6
pour en connaître l'utilité.

Les noms de schéma commençant par pg_ sont réservés pour les besoins du système et ne peuvent
être créés par les utilisateurs.

5.8.2. Le schéma public


Dans les sections précédentes, les tables sont créées sans qu'un nom de schéma soit indiqué. Par défaut,
ces tables (et les autres objets) sont automatiquement placées dans un schéma nommé « public ». Toute
nouvelle base de données contient un tel schéma. Les instructions suivantes sont donc équivalentes :

CREATE TABLE produits ( ... );

et :

CREATE TABLE public.produits ( ... );

5.8.3. Chemin de parcours des schémas


Non seulement l'écriture de noms qualifiés est contraignante, mais il est, de toute façon, préférable de
ne pas fixer un nom de schéma dans les applications. De ce fait, les tables sont souvent appelées par
des noms non qualifiés, soit le seul nom de la table. Le système détermine la table appelée en suivant
un chemin de recherche, liste de schémas dans lesquels chercher. La première table correspondante
est considérée comme la table voulue. S'il n'y a pas de correspondance, une erreur est remontée, quand
bien même il existerait des tables dont le nom corresponde dans d'autres schémas de la base.

La possibilité de créer des objets de même nom dans différents schémas complique l'écriture d'une
requête qui référence précisément les mêmes objets à chaque fois. Cela ouvre aussi la possibilité

83
Définition des données

aux utilisateurs de modifier le comportement des requêtes des autres utilisations, par accident ou
volontairement. À cause de la prévalence des noms non qualifiés dans les requêtes et de leur utilisation
des internes de PostgreSQL, ajouter un schéma à search_path demande en effet à tous les
utilisateurs d'avoir le droit CREATE sur ce schéma. Quand vous exécutez une requête ordinaire, un
utilisateur mal intentionné capable de créer des objets dans un schéma de votre chemin de recherche
peut prendre le contrôle et exécuter des fonctions SQL arbitraires comme si vous les exécutiez.

Le premier schéma du chemin de recherche est appelé schéma courant. En plus d'être le premier
schéma parcouru, il est aussi le schéma dans lequel les nouvelles tables sont créées si la commande
CREATE TABLE ne précise pas de nom de schéma.

Le chemin de recherche courant est affiché à l'aide de la commande :

SHOW search_path;

Dans la configuration par défaut, ceci renvoie :

search_path
--------------
"$user", public

Le premier élément précise qu'un schéma de même nom que l'utilisateur courant est recherché.
En l'absence d'un tel schéma, l'entrée est ignorée. Le deuxième élément renvoie au schéma public
précédemment évoqué.

C'est, par défaut, dans le premier schéma du chemin de recherche qui existe que sont créés les nouveaux
objets. C'est la raison pour laquelle les objets sont créés, par défaut, dans le schéma public. Lorsqu'il
est fait référence à un objet, dans tout autre contexte, sans qualification par un schéma (modification de
table, modification de données ou requêtes), le chemin de recherche est traversé jusqu'à ce qu'un objet
correspondant soit trouvé. C'est pourquoi, dans la configuration par défaut, tout accès non qualifié ne
peut que se référer au schéma public.

Pour ajouter un schéma au chemin, on écrit :

SET search_path TO mon_schema,public;

($user est omis à ce niveau, car il n'est pas immédiatement nécessaire.) Il est alors possible d'accéder
à la table sans qu'elle soit qualifiée par un schéma :

DROP TABLE ma_table;

Puisque mon_schema est le premier élément du chemin, les nouveaux objets sont, par défaut, créés
dans ce schéma.

On peut aussi écrire :

SET search_path TO mon_schema;

Dans ce cas, le schéma public n'est plus accessible sans qualification explicite. Hormis le fait qu'il
existe par défaut, le schéma public n'a rien de spécial. Il peut même être effacé.

On peut également se référer à la Section 9.25 qui détaille les autres façons de manipuler le chemin
de recherche des schémas.

Le chemin de recherche fonctionne de la même façon pour les noms de type de données, les noms de
fonction et les noms d'opérateur que pour les noms de table. Les noms des types de données et des
fonctions peuvent être qualifiés de la même façon que les noms de table. S'il est nécessaire d'écrire un
nom d'opérateur qualifié dans une expression, il y a une condition spéciale. Il faut écrire :

OPERATOR(schéma.opérateur)

Cela afin d'éviter toute ambiguïté syntaxique. Par exemple :

84
Définition des données

SELECT 3 OPERATOR(pg_catalog.+) 4;

En pratique, il est préférable de s'en remettre au chemin de recherche pour les opérateurs, afin de ne
pas avoir à écrire quelque chose d'aussi étrange.

5.8.4. Schémas et privilèges


Par défaut, les utilisateurs ne peuvent pas accéder aux objets présents dans les schémas qui ne
leur appartiennent pas. Pour le permettre, le propriétaire du schéma doit donner le droit USAGE
sur le schéma. Pour autoriser les utilisateurs à manipuler les objets d'un schéma, des privilèges
supplémentaires doivent éventuellement être accordés, en fonction de l'objet.

Un utilisateur peut aussi être autorisé à créer des objets dans le schéma d'un autre. Pour cela, le privilège
CREATE sur le schéma doit être accordé. Par défaut, tout le monde bénéficie des droits CREATE et
USAGE sur le schéma public. Cela permet à tous les utilisateurs qui peuvent se connecter à une base
de données de créer des objets dans son schéma public. Certaines méthodes d'usage demandent à
révoquer ce droit :

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

Le premier « public » est le schéma, le second « public » signifie « tout utilisateur ». Dans le premier
cas, c'est un identifiant, dans le second, un mot-clé, d'où la casse différente. (Se reporter aux règles
de la Section 4.1.1.)

5.8.5. Le schéma du catalogue système


En plus du schéma public et de ceux créés par les utilisateurs, chaque base de données contient un
schéma pg_catalog. Celui-ci contient les tables système et tous les types de données, fonctions
et opérateurs intégrés. pg_catalog est toujours dans le chemin de recherche. S'il n'est pas nommé
explicitement dans le chemin, il est parcouru implicitement avant le parcours des schémas du chemin.
Cela garantit que les noms internes sont toujours accessibles. En revanche, pg_catalog peut être
explicitement placé à la fin si les noms utilisateur doivent surcharger les noms internes.

Comme les noms des catalogues système commencent par pg_, il est préférable d'éviter d'utiliser
de tels noms pour se prémunir d'éventuels conflits si une version ultérieure devait définir une table
système qui porte le même nom que la table créée. (Le chemin de recherche par défaut implique qu'une
référence non qualifiée à cette table pointe sur la table système). Les tables système continueront de
suivre la convention qui leur impose des noms préfixés par pg_. Il n'y a donc pas de conflit possible
avec des noms de table utilisateur non qualifiés, sous réserve que les utilisateurs évitent le préfixe pg_.

5.8.6. Utilisation
Les schémas peuvent être utilisés de différentes façons pour organiser les données. Un modèle
d'utilisation de schéma sécurisé empêche tous les utilisateurs pour lesquels nous n'avons pas confiance
de modifier le comportement des requêtes des autres utilisateurs. Quand une base de données n'utilise
pas de modèle d'utilisation de schéma sécurisé, les utilisateurs souhaitant interroger cette base de
données en toute sécurité devront prendre des mesures de protection au début de chaque session. Plus
précisément, ils commenceraient chaque session par la configuration du paramètre search_path
en une chaîne vide ou en supprimant de leur search_path les schémas accessibles en écriture par
des utilisateurs standards. Il existe quelques modèles d'utilisation facilement pris en charge par la
configuration par défaut :

• Contraindre les utilisateurs ordinaires aux schémas privés des utilisateurs. Pour cela, exécutez
REVOKE CREATE ON SCHEMA public FROM PUBLIC, et créez un schéma pour
chaque utilisateur, du nom de cet utilisateur. Rappelez-vous que le chemin de recherche par défaut
commence avec $user, qui se résout avec le nom de l'utilisateur. Par conséquent, si chaque
utilisateur a un schéma distinct, ils accèdent à leur propres schémas par défaut. Après avoir
adopté ce modèle dans une base de données où des utilisateurs non fiables se sont déjà connectés,
pensez à vérifier dans le schéma public l'existence d'objets nommés comme des objets du schéma

85
Définition des données

pg_catalog. Ce modèle est sécurisé sauf si un utilisateur non fiable est le propriétaire de la base
de données ou détient l'attribut CREATEROLE, auquel cas aucun modèle d'utilisation de schéma
sécurisé n'existe.

• Supprimer le schéma public du chemin de recherche par défaut, en modifiant le fichier


postgresql.conf ou en exécutant ALTER ROLE ALL SET search_path = "$user".
Tout le monde conserve la possibilité de créer des objets dans le schéma public, mais seuls les noms
qualifiés choisiront ces objets. Bien que les références de table qualifiées soient correctes, les appels
aux fonctions dans le le schéma public seront dangereux ou peu fiables. Si vous créez des fonctions
ou des extensions dans le schéma public, utilisez le premier modèle à la place. Sinon, tout comme
le premier modèle, c'est sécurisé sauf si un utilisateur non fiable est le propriétaire de la base de
données ou détient l'attribut CREATEROLE.

• Conserver la valeur par défaut. Tous les utilisateurs ont accès au schéma public implicitement. Ceci
simule la situation où les schémas ne sont pas du tout disponibles, réalisant ainsi une transition en
douceur vers un monde qui ne connait pas les schémas. Néanmoins, ceci ne sera jamais un modèle
sécurisé. C'est uniquement acceptable si la base de données ne contient qu'un seul utilisateur ou
quelques utilisateurs qui se font mutuellement confiance.

Pour chaque méthode, pour installer des applications partagées (tables utilisées par tout le monde,
fonctions supplémentaires fournies par des tiers, etc.), placez-les dans des schémas séparés. N'oubliez
pas d'accorder les droits appropriés pour permettre aux autres utilisateurs d'y accéder. Les utilisateurs
peuvent ensuite faire référence à ces objets supplémentaires en les qualifiant avec le nom du schéma,
ou bien ils peuvent placer les schémas supplémentaires dans leur chemin de recherche, suivant leur
préférence.

5.8.7. Portabilité
Dans le standard SQL, la notion d'objets d'un même schéma appartenant à des utilisateurs différents
n'existe pas. De plus, certaines implantations ne permettent pas de créer des schémas de nom différent
de celui de leur propriétaire. En fait, les concepts de schéma et d'utilisateur sont presque équivalents
dans un système de base de données qui n'implante que le support basique des schémas tel que
spécifié dans le standard. De ce fait, beaucoup d'utilisateurs considèrent les noms qualifiés comme
correspondant en réalité à utilisateur.table. C'est comme cela que PostgreSQL se comporte
si un schéma utilisateur est créé pour chaque utilisateur.

Le concept de schéma public n'existe pas non plus dans le standard SQL. Pour plus de conformité
au standard, le schéma public ne devrait pas être utilisé.

Certains systèmes de bases de données n'implantent pas du tout les schémas, ou fournissent le support
de namespace en autorisant (peut-être de façon limitée) l'accès inter-bases de données. Dans ce cas,
la portabilité maximale est obtenue en n'utilisant pas les schémas.

5.9. L'héritage
PostgreSQL implante l'héritage des tables, qui peut s'avérer très utile pour les concepteurs de bases
de données. (SQL:1999 et les versions suivantes définissent une fonctionnalité d'héritage de type qui
diffère par de nombreux aspects des fonctionnalités décrites ici.)

Soit l'exemple d'un modèle de données de villes. Chaque état comporte plusieurs villes, mais une seule
capitale. Pour récupérer rapidement la ville capitale d'un état donné, on peut créer deux tables, une
pour les capitales et une pour les villes qui ne sont pas des capitales. Mais, que se passe-t-il dans le cas
où toutes les données d'une ville doivent être récupérées, qu'elle soit une capitale ou non ? L'héritage
peut aider à résoudre ce problème. La table capitales est définie pour hériter de villes :

CREATE TABLE villes (


nom text,
population float,
elevation int -- (en pied)

86
Définition des données

);

CREATE TABLE capitales (


etat char(2)
) INHERITS (villes);

Dans ce cas, la table capitales hérite de toutes les colonnes de sa table parente, villes. Les
capitales ont aussi une colonne supplémentaire, etat, qui indique l'état dont elles sont capitales.

Dans PostgreSQL, une table peut hériter de zéro à plusieurs autres tables et une requête faire référence
aux lignes d'une table ou à celles d'une table et de ses descendantes. Ce dernier comportement est
celui par défaut.

Par exemple, la requête suivante retourne les noms et elevations de toutes les villes, y compris les
capitales, situées à une élévation supérieure à 500 pieds :

SELECT nom, elevation


FROM villes
WHERE elevation > 500;

Avec les données du tutoriel de PostgreSQL (voir Section 2.1), ceci renvoie :

nom | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845

D'un autre côté, la requête suivante retourne les noms et elevations de toutes les villes, qui ne sont pas
des capitales, situées à une élévation supérieure à 500 pieds :

SELECT nom, elevation


FROM ONLY villes
WHERE elevation > 500;

nom | elevation
-----------+-----------
Las Vegas | 2174
Mariposa | 1953

Le mot-clé ONLY indique que la requête s'applique uniquement aux villes, et non pas à toutes les
tables en dessous de villes dans la hiérarchie de l'héritage. Un grand nombre des commandes déjà
évoquées -- SELECT, UPDATE et DELETE -- supportent le mot-clé ONLY.

Vous pouvez aussi écrire le nom de la table avec un astérisque (*) à la fin pour indiquer spécifiquement
que les tables filles sont incluses :

SELECT name, elevation


FROM cities*
WHERE elevation > 500;

Écrire l'astérisque (*) n'est pas nécessaire, puisque ce comportement est toujours le comportement par
défaut. Toutefois, cette syntaxe est toujours supportée pour raison de compatibilité avec les anciennes
versions où le comportement par défaut pouvait être changé.

Dans certains cas, il peut être intéressant de savoir de quelle table provient une ligne donnée. Une
colonne système appelée TABLEOID, présente dans chaque table, donne la table d'origine :

SELECT v.tableoid, v.nom, v.elevation


FROM villes v
WHERE v.elevation > 500;

87
Définition des données

qui renvoie :

tableoid | nom | elevation


----------+-----------+-----------
139793 | Las Vegas | 2174
139793 | Mariposa | 1953
139798 | Madison | 845

(Reproduire cet exemple conduit probablement à des OID numériques différents). Une jointure avec
pg_class, permet d'obtenir les noms réels des tables :

SELECT p.relname, v.nom, v.elevation


FROM villes v, pg_class p
WHERE v.elevation > 500 AND v.tableoid = p.oid;

ce qui retourne :

relname | nom | elevation


-----------+-----------+-----------
villes | Las Vegas | 2174
villes | Mariposa | 1953
capitales | Madison | 845

Une autre manière d'obtenir le même effet est d'utiliser le pseudo-type regclass qui affichera l'OID
de la table de façon symbolique :

SELECT v.tableoid::regclass, v.nom, v.elevation


FROM villes v
WHERE v.elevation > 500;

L'héritage ne propage pas automatiquement les données des commandes INSERT ou COPY aux autres
tables de la hiérarchie de l'héritage. Dans l'exemple considéré, l'instruction INSERT suivante échoue :

INSERT INTO villes (nom, population, elevation, etat)


VALUES ('Albany', NULL, NULL, 'NY');

On pourrait espérer que les données soient d'une manière ou d'une autre acheminées vers la table
capitales, mais ce n'est pas le cas : INSERT insère toujours dans la table indiquée. Dans certains
cas, il est possible de rediriger l'insertion en utilisant une règle (voir Chapitre 41). Néanmoins, cela
n'est d'aucune aide dans le cas ci-dessus, car la table villes ne contient pas la colonne etat. La
commande est donc rejetée avant que la règle ne puisse être appliquée.

Toutes les contraintes de vérification et toutes les contraintes NOT NULL sur une table parent sont
automatiquement héritées par les tables enfants, sauf si elles sont spécifiées explicitement avec des
clauses NO INHERIT. Les autres types de contraintes (unicité, clé primaire, clé étrangère) ne sont
pas hérités.

Une table peut hériter de plusieurs tables, auquel cas elle possède l'union des colonnes définies par les
tables mères. Toute colonne déclarée dans la définition de la table enfant est ajoutée à cette dernière. Si
le même nom de colonne apparaît dans plusieurs tables mères, ou à la fois dans une table mère et dans
la définition de la table enfant, alors ces colonnes sont « assemblées » pour qu'il n'en existe qu'une dans
la table enfant. Pour être assemblées, les colonnes doivent avoir le même type de données, sinon une
erreur est levée. Les contraintes de vérification et les contraintes non NULL héritables sont assemblées
de façon similaire. De ce fait, par exemple, une colonne assemblée sera marquée non NULL si une des
définitions de colonne d'où elle provient est marquée non NULL. Les contraintes de vérification sont
assemblées si elles ont le même nom, et l'assemblage échouera si leurs conditions sont différentes.

L'héritage de table est établi à la création de la table enfant, à l'aide de la clause INHERITS de
l'instruction CREATE TABLE. Alternativement, il est possible d'ajouter à une table, définie de façon

88
Définition des données

compatible, une nouvelle relation de parenté à l'aide de la clause INHERIT de ALTER TABLE. Pour
cela, la nouvelle table enfant doit déjà inclure des colonnes de mêmes nom et type que les colonnes
de la table parent. Elle doit aussi contenir des contraintes de vérification de mêmes nom et expression
que celles de la table parent.

De la même façon, un lien d'héritage peut être supprimé d'un enfant à l'aide de la variante NO
INHERIT d'ALTER TABLE. Ajouter et supprimer dynamiquement des liens d'héritage de cette
façon est utile quand cette relation d'héritage est utilisée pour le partitionnement des tables (voir
Section 5.10).

Un moyen pratique de créer une table compatible en vue d'en faire ultérieurement une table enfant est
d'utiliser la clause LIKE dans CREATE TABLE. Ceci crée une nouvelle table avec les mêmes colonnes
que la table source. S'il existe des contraintes CHECK définies sur la table source, l'option INCLUDING
CONSTRAINTS de LIKE doit être indiquée, car le nouvel enfant doit avoir des contraintes qui
correspondent à celles du parent pour être considéré compatible.

Une table mère ne peut pas être supprimée tant qu'elle a des enfants. Pas plus que les colonnes ou
les contraintes de vérification des tables enfants ne peuvent être supprimées ou modifiées si elles
sont héritées. La suppression d'une table et de tous ses descendants peut être aisément obtenue en
supprimant la table mère avec l'option CASCADE (voir Section 5.13).

ALTER TABLE propage toute modification dans les définitions des colonnes et contraintes de
vérification à travers la hiérarchie d'héritage. Là encore, supprimer des colonnes qui dépendent
d'autres tables mères n'est possible qu'avec l'option CASCADE. ALTER TABLE suit les mêmes règles
d'assemblage de colonnes dupliquées et de rejet que l'instruction CREATE TABLE.

Les requêtes sur tables héritées réalisent des vérifications de droit sur la table parent seulement. De
ce fait, par exemple, donner le droit UPDATE sur la table villes implique que les droits de mise
à jour des lignes dans la table capitales soient elles aussi vérifiées quand elles sont accédées via
la table villes. Ceci préserve l'apparence que les données proviennent (aussi) de la table parent.
Mais la table capitales ne pouvait pas être mise à jour directement sans droit supplémentaire.
Deux exceptions à cette règle sont TRUNCATE et LOCK TABLE, où les droits sur les tables filles sont
toujours vérifiées qu'elles soient traitées directement ou par récursivité via les commandes réalisées
sur la table parent.

De façon similaire, les politiques de sécurité au niveau ligne de la table parent (voir Section 5.7) sont
appliquées aux lignes provenant des tables filles avec une requête héritée. Les politiques de tables
enfant sont appliquées seulement quand la table enfant est explicitement nommée dans la requête.
Dans ce cas, toute politique attachée à ses parents est ignorée.

Les tables distantes (voir Section 5.11) peuvent aussi participer aux hiérarchies d'héritage, soit comme
table parent, soit comme table enfant, comme les tables standards peuvent l'être. Si une table distante
fait partie d'une hiérarchie d'héritage, toutes les opérations non supportées par la table étrangère ne
sont pas non plus supportées sur l'ensemble de la hiérarchie.

5.9.1. Restrictions
Notez que toutes les commandes SQL fonctionnent avec les héritages. Les commandes utilisées
pour récupérer des données, pour modifier des données ou pour modifier le schéma (autrement dit
SELECT, UPDATE, DELETE, la plupart des variantes de ALTER TABLE, mais pas INSERT ou
ALTER TABLE ... RENAME) incluent par défaut les tables filles et supportent la notation ONLY
pour les exclure. Les commandes qui font de la maintenance de bases de données et de la configuration
(par exemple REINDEX, VACUUM) fonctionnent typiquement uniquement sur les tables physiques,
individuelles et ne supportent pas la récursion sur les tables de l'héritage. Le comportement respectif
de chaque commande individuelle est documenté dans la référence (Commandes SQL).

Il existe une réelle limitation à la fonctionnalité d'héritage : les index (dont les contraintes d'unicité)
et les contraintes de clés étrangères ne s'appliquent qu'aux tables mères, pas à leurs héritiers. Cela est
valable pour le côté référençant et le côté référencé d'une contrainte de clé étrangère. Ce qui donne,
dans les termes de l'exemple ci-dessus :

89
Définition des données

• si villes.nom est déclarée UNIQUE ou clé primaire (PRIMARY KEY), cela n'empêche pas la
table capitales de posséder des lignes avec des noms dupliqués dans villes. Et ces lignes
dupliquées s'affichent par défaut dans les requêtes sur villes. En fait, par défaut, capitales
n'a pas de contrainte d'unicité du tout et, du coup, peut contenir plusieurs lignes avec le même nom.
Une contrainte d'unicité peut être ajoutée à capitales, mais cela n'empêche pas la duplication
avec villes ;

• de façon similaire, si villes.nom fait référence (REFERENCES) à une autre table, cette contrainte
n'est pas automatiquement propagée à capitales. Il est facile de contourner ce cas de figure en
ajoutant manuellement la même contrainte REFERENCES à capitales ;

• si une autre table indique REFERENCES villes(nom), cela l'autorise à contenir les noms des
villes, mais pas les noms des capitales. Il n'existe pas de contournement efficace de ce cas.

Certaines fonctionnalités non implémentées pour les hiérarchies d'héritage sont disponibles pour le
partitionnement déclaratif. Il est de ce fait nécessaire de réfléchir consciencieusement à l'utilité de
l'héritage pour une application donnée.

5.10. Partitionnement de tables


PostgreSQL offre un support basique du partitionnement de table. Cette section explique pourquoi et
comment implanter le partitionnement lors de la conception de la base de données.

5.10.1. Aperçu
Le partitionnement fait référence à la division d'une table logique volumineuse en plusieurs parties
physiques plus petites. Le partitionnement comporte de nombreux avantages :

• les performances des requêtes peuvent être significativement améliorées dans certaines situations,
particulièrement lorsque la plupart des lignes fortement accédées d'une table se trouvent sur une
seule partition ou sur un petit nombre de partitions. Le partitionnement se substitue aux niveaux
élevés de index, facilitant la tenue en mémoire des parties les plus utilisées de l'index ;

• lorsque les requêtes ou les mises à jour accèdent à un pourcentage important d'une seule partition,
les performances peuvent être grandement améliorées par l'utilisation avantageuse d'un parcours
séquentiel sur cette partition plutôt que d'utiliser un index qui nécessiterait des lectures aléatoires
réparties sur toute la table ;

• les chargements et suppressions importants de données peuvent être obtenus par l'ajout ou la
suppression de partitions, sous réserve que ce besoin ait été pris en compte lors de la conception du
partitionnement. Supprimer une partition individuelle en utilisant DROP TABLE ou en exécutant
ALTER TABLE DETACH PARTITION est bien plus rapide qu'une opération groupée. Cela évite
également la surcharge due au VACUUM causé par un DELETE massif ;

• les données peu utilisées peuvent être déplacées sur un média de stockage moins cher et plus lent.

Ces bénéfices ne sont réellement intéressants que si cela permet d'éviter une table autrement plus
volumineuse. Le point d'équilibre exact à partir duquel une table tire des bénéfices du partitionnement
dépend de l'application. Toutefois, le partitionnement doit être envisagé si la taille de la table peut être
amenée à dépasser la taille de la mémoire physique du serveur.

PostgreSQL offre un support natif pour les formes suivantes de partitionnement :

Partitionnement par intervalles

La table est partitionnée en « intervalles » (ou échelles) définis par une colonne clé ou par un
ensemble de colonnes, sans recouvrement entre les intervalles de valeurs affectées aux différentes
partitions. Il est possible, par exemple, de partitionner par intervalles de date ou par intervalles
d'identifiants pour des objets métier particuliers. Chaque limite de l'intervalle est comprise comme

90
Définition des données

étant inclusive au point initial et exclusive au point final. Par exemple, si l'intervalle d'une partition
va de 1 à 10, et que le prochain intervalle va de 10 à 20, alors la valeur 10 appartient à la
deuxième partition, et non pas à la première.

Partitionnement par liste

La table est partitionnée en listant explicitement les valeurs clés qui apparaissent dans chaque
partition.

Partitionnement par hachage

La table est partitionnée en spécifiant un module et un reste pour chaque partition. Chaque
partition contiendra les lignes pour lesquelles la valeur de hachage de la clé de partition divisée
par le module spécifié produira le reste spécifié.

Si votre application nécessite d'utiliser d'autres formes de partitionnement qui ne sont pas listées au-
dessus, des méthodes alternatives comme l'héritage et des vues UNION ALL peuvent être utilisées à la
place. De telles méthodes offrent de la flexibilité, mais n'ont pas certains des bénéfices de performance
du partitionnement déclaratif natif.

5.10.2. Partitionnement déclaratif


PostgreSQL donne un moyen de déclarer qu'une table est divisée en partitions. La table qui est divisée
est appelée table partitionnée. La déclaration inclut la méthode de partitionnement, comme décrite ci-
dessus, et une liste de colonnes ou d'expressions à utiliser comme clé de partitionnement.

La table partitionnée est elle-même une table « virtuelle » sans stockage propre. À la place, le stockage
se fait dans les to partitions, qui sont en fait des tables ordinaires mais associées avec la table
partitionnée. Chaque partition enregistre un sous-ensemble de données correspondant à la définition
de ses limites de partition. Tous les lignes insérées dans une table partitionnée seront transférées sur la
partition appropriée suivant les valeurs des colonnes de la clé de partitionnement. Mettre à jour la clé
de partitionnement d'une ligne causera son déplacement dans une partition différente si elle ne satisfait
plus les limites de sa partition originale.

Les partitions peuvent elles-mêmes être définies comme des tables partitionnées, ce qui aboutirait à du
sous-partitionnement. Bien que toutes les partitions doivent avoir les mêmes clonnes que leur parent
partitionné, es partitions peuvent avoir leurs propres index, contraintes et valeurs par défaut, différents
de ceux des autres partitions. Voir CREATE TABLE pour plus de détails sur la création des tables
partitionnées et des partitions.

Il n'est pas possible de transformer une table standard en table partitionnée et inversement. Par contre,
il est possible d'ajouter une table standard ou une table partitionnée existante comme une partition
d'une table partitionnée, ou de supprimer une partition d'une table partitionnée, pour la transformer
en table standard ; ceci peut simplifier et accélérer de nombreux traitements de maintenance. Voir
ALTER TABLE pour en apprendre plus sur les sous-commandes ATTACH PARTITION et DETACH
PARTITION.

Les partitions peuvent également être des tables étrangères, mais il faut faire très attention car c'est de
la responsabilité de l'utilisateur que le contenu de la table distante satisfasse la clé de partitionnement.
Il existe aussi d'autres restrictions. Voir CREATE FOREIGN TABLE pour plus d'informations.

5.10.2.1. Exemple
Imaginons que nous soyons en train de construire une base de données pour une grande société de
crème glacée. La société mesure les pics de températures chaque jour, ainsi que les ventes de crème
glacée dans chaque région. Conceptuellement, nous voulons une table comme ceci :

CREATE TABLE mesure (


id_ville int not null,
date_trace date not null,

91
Définition des données

temperature int,
ventes int
);

La plupart des requêtes n'accèdent qu'aux données de la dernière semaine, du dernier mois ou du
dernier trimestre, car cette table est essentiellement utilisée pour préparer des rapports en ligne pour
la direction. Pour réduire le nombre de données anciennes à stocker, seules les trois dernières années
sont conservées. Au début de chaque mois, les données du mois le plus ancien sont supprimées. Dans
cette situation, le partitionnement permet de répondre aux différents besoins identifiés sur la table des
mesures.

Pour utiliser le partitionnement déclaratif dans ce cas d'utilisation, il faut suivre les étapes suivantes :

1. Créer une table measurement comme une table partitionnée en spécifiant la clause PARTITION
BY, ce qui inclut la méthode de partitionnement ( RANGE dans ce cas) ainsi que la liste de la ou
des colonnes à utiliser comme clé de partitionnement.

+CREATE TABLE measurement (


city_id int not null,
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (logdate);

2. Créez les partitions. La définition de chaque partition doit spécifier les limites qui correspondent
à la méthode de partitionnement ainsi qu'à la clé de partitionnement du parent. Veuillez noter que
spécifier des limites telles que les valeurs de la nouvelle partition pourront se chevaucher avec
celles d'une ou plusieurs autres partitions retournera une erreur.

Les partitions ainsi créées sont de tous les points de vue des tables PostgreSQL normales (ou,
potentiellement, des tables étrangères). Il est possible de spécifier un tablespace et des paramètres
de stockage pour chacune des partitions séparément.

Pour notre exemple, chaque partition devrait contenir un mois de données pour correspondre au
besoin de supprimer un mois de données à la fois. Les commandes pourraient ressembler à ceci :

CREATE TABLE measurement_y2006m02 PARTITION OF measurement


FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');

CREATE TABLE measurement_y2006m03 PARTITION OF measurement


FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');

...
CREATE TABLE measurement_y2007m11 PARTITION OF measurement
FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');

CREATE TABLE measurement_y2007m12 PARTITION OF measurement


FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
TABLESPACE fasttablespace;

CREATE TABLE measurement_y2008m01 PARTITION OF measurement


FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
WITH (parallel_workers = 4)
TABLESPACE fasttablespace;

(Pour rappel, les partitions adjacentes peuvent partager une valeur de limite car les limites hautes
sont traitées comme des limites exclusive.)

92
Définition des données

Si vous voulez mettre en place du sous-partitionnement, spécifiez la clause PARTITION BY dans


les commandes utilisées pour créer des partitions individuelles, par exemple :

CREATE TABLE measurement_y2006m02 PARTITION OF measurement


FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
PARTITION BY RANGE (peaktemp);

Après avoir créé les partitions de measurement_y2006m02, toute donnée insérée dans
measurement qui correspond à measurement_y2006m02 (ou donnée qui est directement
insérée dans measurement_y2006m02, ce qui est autorié à condition que la contrainte de
partition soit respectée) sera redirigée vers l'une de ses partitions en se basant sur la colonne
peaktemp. La clé de partition spécifiée pourrait se chevaucher avec la clé de partition du parent,
il faut donc faire spécialement attention lorsque les limites d'une sous-partition sont spécifiées afin
que l'ensemble de données qu'elle accepte constitue un sous-ensemble de ce que les propres limites
de la partition acceptent ; le système n'essayera pas de vérifier si c'est vraiment le cas.

Insérer des données dans la table parent, données qui ne correspondent pas à une des partitions
existantes, causera une erreur ; une partition appropriée doit être ajoutée manuellement.

Il n'est pas nécessaire de créer manuellement les contraintes de table décrivant les conditions des
limites de partition pour les partitions. De telles contraintes seront créées automatiquement.
3. Créez un index sur la ou les colonnes de la clé, ainsi que tout autre index que vous pourriez vouloir
pour chaque partition. (L'index sur la clé n'est pas strictement nécessaire, mais c'est utile dans la
plupart des scénarios.) Ceci crée automatiquement un index correspondant sur chaque partition, et
toutes les partitions que vous créerez ou attacherez plus tard auront elles-aussi cet index.

CREATE INDEX ON measurement (logdate);

Assurez-vous que le paramètre de configuration enable_partition_pruning ne soit pas désactivé


dans postgresql.conf. S'il l'est, les requêtes ne seront pas optimisées comme voulu.

Dans l'exemple ci-dessus, nous créerions une nouvelle partition chaque mois, il serait donc avisé
d'écrire un script qui génère le DDL nécessaire automatiquement.

5.10.2.2. Maintenance des partitions


Normalement, l'ensemble des partitions établies lors de la définition initiale de la table n'a pas vocation
à demeurer statique. Il est courant de vouloir supprimer les partitions contenant d'anciennes données
et d'ajouter périodiquement de nouvelles partitions pour de nouvelles données. Un des avantages les
plus importants du partitionnement est précisément qu'il permet d'exécuter instantanément cette tâche
de maintenance normalement pénible, en manipulant la structure partitionnée, plutôt que de bouger
physiquement de grands ensembles de données.

Le moyen le plus simple pour supprimer d'anciennes données est de supprimer la partition qui n'est
plus nécessaire :

DROP TABLE measurement_y2006m02;

Cela peut supprimer des millions d'enregistrements très rapidement, car il n'est pas nécessaire de
supprimer chaque enregistrement séparément. Veuillez noter toutefois que la commande ci-dessus
nécessite de prendre un verrou de type ACCESS EXCLUSIVE sur la table parente.

Une autre possibilité, généralement préférable, est de ne pas supprimer la partition de la table
partitionnée, mais de la conserver en tant que table à part entière :

93
Définition des données

ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;

Cela permet d'effectuer ensuite d'autres opérations sur les données avant de la supprimer. Par exemple,
il s'agit souvent du moment idéal pour sauvegarder les données en utilisant COPY, pg_dump, ou des
outils similaires. Cela pourrait également être le bon moment pour agréger les données dans un format
moins volumineux, effectuer d'autres manipulations de données ou exécuter des rapports.

De la même manière, nous pouvons ajouter une nouvelle partition pour gérer les nouvelles données.
Nous pouvons créer une partition vide dans la table partitionnée exactement comme les partitions
originales ont été créées précédemment :

CREATE TABLE measurement_y2008m02 PARTITION OF measurement


FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
TABLESPACE fasttablespace;

De manière alternative, il est parfois plus utile de créer la nouvelle table en dehors de la structure
de la partition, et d'en faire une partition plus tard. Cela permet de charger de nouvelles données,
de les vérifier et d'y effectuer des transformations avant que les données apparaissent dans la table
partitionnée. L'option CREATE TABLE ... LIKE est utile pour éviter de répéter à chaque fois
la définition de la table parent :

CREATE TABLE measurement_y2008m02


(LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
TABLESPACE fasttablespace;

ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02


CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE
'2008-03-01' );

\copy measurement_y2008m02 from 'measurement_y2008m02'


-- possibly some other data preparation work

ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02


FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );

Avant d'exécuter une commande ATTACH PARTITION, il est recommandé de créer une contrainte
CHECK sur la table qui doit être attachée correspondant à la contrainte de la partition désirée. De
cette manière, le système n'aura pas besoin d'effectuer un parcours de la table qui est habituellement
nécessaire pour valider la contrainte implicite de partition. Sans la contrainte CHECK, la table sera
parcourue pour valider la contrainte de partition, alors qu'elle aura pris un verrou de niveau ACCESS
EXCLUSIVE sur la table parente. Il est recommandé de supprimer la contrainte CHECK redondante
après la fin de la commande ATTACH PARTITION.

Comme expliqué ci-dessus, il est possible de créer des index sur les tables partitionnées pour qu'elles
soient automatiquement appliqués à la hiérarchie complète. Ceci est très pratique car, non seulement
les partitions existantes seront indexées, mais aussi toute nouvelle partition le sera. La seule limitation
est qu'il n'est pas possible d'utiliser la clause CONCURRENTLY lors de la création d'un tel index
partitionné. Pour éviter de longues périodes de verrous, il est possible d'utiliser CREATE INDEX
ON ONLY sur la table partitionnée ; un tel index est marqué invalide et les partitions l'obtiennent pas
automatiquement l'index. Les index sur les partitions peuvent être créés individuellement en utilisant
CONCURRENTLY, puis être attachés à l'index sur le parent en utilisant ALTER INDEX .. ATTACH
PARTITION. Une fois que les index des partitions sont attachés à l'index parent, l'index parent est
marqué valide automatiquement. Par exemple :

94
Définition des données

CREATE INDEX measurement_usls_idx ON ONLY measurement (unitsales);

CREATE INDEX measurement_usls_200602_idx


ON measurement_y2006m02 (unitsales);
ALTER INDEX measurement_usls_idx
ATTACH PARTITION measurement_usls_200602_idx;
...

Cette technique peut aussi être utilisée avec les contraintes UNIQUE et PRIMARY KEY ; les index
sont créés implicitement quand la contrainte est créer. Par exemple :

ALTER TABLE ONLY measurement ADD UNIQUE (city_id, logdate);

ALTER TABLE measurement_y2006m02 ADD UNIQUE (city_id, logdate);


ALTER INDEX measurement_city_id_logdate_key
ATTACH PARTITION measurement_y2006m02_city_id_logdate_key;
...

5.10.2.3. Limitations
Les limitations suivantes s'appliquent aux tables partitionnées :

• Pour créer une contrainte d'unicité ou de clé primaire sur une table partitionnée, la clé de
partitionnement ne doit pas inclure d'expressions ou d'appels de fonction, et les colonnes de la
contrainte doivent inclure toutes les colonnes de la clé de partitionnement. Cette limitation existe
parce que les index individuels forçant la contrainte peuvent seulement garantir l'unicité dans leur
propre partition ; de ce fait, la structure même de la partition doit garantir qu'il n'y aura pas de
duplicats dans les différentes partitions.

• Il n'existe aucun moyen de créer une contrainte d'exclusion sur toute la table partitionnée. Il
est seulement possible de placer une telle contrainte sur chaque partition individuellement. Cette
limitation vient là-aussi de l'impossibilité de fixer les restrictions entre partitions.

• Alors que les clés primaires sont prises en charge sur les tables partitionnées, les clés étrangères
faisant référence à des tables partitionnées ne sont pas prises en charge. (Les références de clés
étrangères d'une table partitionnée vers une autre table sont supportées.)

• en cas de besoin, les triggers BEFORE ROW doivent être définies sur des partitions individuelles,
et non sur la table partitionnée.

• Mélanger des relations temporaires et permanentes dans la même arborescence de partitions n'est
pas autorisé. Par conséquent, si une table partitionnée est permanente, ses partitions doivent l'être
aussi ; de même si la table partitionnée est temporaire, ses partitions doivent l'être aussi. Lors de
l'utilisation de relations temporaires, tous les membres de l'arborescence des partitions doivent être
issus de la même session.

Les partitions individuelles sont liées à leur table partitionnée en utilisant l'héritage en arrière plan.
Néanmoins, il n'est pas possible d'utiliser toutes les fonctionnalités génériques de l'héritage avec les
tables en partitionnement déclaratif et leurs partitions, comme indiqué ci-dessous. Notamment, une
partition ne peut pas avoir d'autres parents que leur table partitionnée. Une table ne peut pas non plus
hériter d'une table partitionnée et d'une table normale. Cela signifie que les tables partitionnées et leur
partitions ne partagent jamais une hiérarchie d'héritage avec des tables normales.

Comme une hiérarchie de partitionnement consistant en la table partitionnée et ses partitions est
toujours une hiérarchie d'héritage, tableoid et toutes les règles normales d'héritage s'appliquent
comme décrites dans Section 5.9, avec quelques exceptions :

95
Définition des données

• Les partitions ne peuvent pas avoir des colonnes qui ne sont pas présentes chez le parent. Il n'est
pas possible d'indiquer des colonnes lors de la création de partitions avec CREATE TABLE, pas
plus qu'il n'est possible d'ajouter des colonnes aux partitions après leur création en utilisant ALTER
TABLE. Les tables pourraient être ajoutées en tant que partition avec ALTER TABLE ...
ATTACH PARTITION seulement si leurs colonnes correspondent exactement à leur parent, en
incluant toute colonne oid.

• Les contraintes CHECK et NOT NULL d'une table partitionnée sont toujours héritées par toutes ses
partitions. La création des contraintes CHECK marquées NO INHERIT n'est pas autorisée sur les
tables partitionnées. Vous ne pouvez pas supprimer une contrainte NOT NULL de la colonne d'une
partition si la même contrainte est présente dans la table parent.

• Utiliser ONLY pour ajouter ou supprimer une contrainte uniquement sur la table partitionnée est
supportée tant qu'il n'y a pas de partitions. Dès qu'une partition existe, utiliser ONLY renverra une
erreur. À la place, des constraintes sur les partitions elles-mêmes peuvent être ajoutées et (si elles
ne sont pas présentes sur la table parent) supprimées.

• Comme une table partitionnée n'a pas de données elle-même, toute tentative d'utiliser TRUNCATE
ONLY sur une table partitionnée renverra systématiquement une erreur.

5.10.3. Partitionnement utilisant l'héritage


Bien que le partitionnement déclaratif natif soit adapté pour la plupart des cas d'usage courant, il y a
certains cas où une approche plus flexible peut être utile. Le partitionnement peut être implémenté en
utilisant l'héritage de table, ce qui permet d'autres fonctionnalités non supportées par le partitionnement
déclaratif, comme :

• Pour le partitionnement déclaratif, les partitions doivent avoir exactement les mêmes colonnes que
la table partitionnée, alors qu'avec l'héritage de table, les tables filles peuvent avoir des colonnes
supplémentaires non présentes dans la table parente.

• L'héritage de table permet l'héritage multiple.

• Le partitionnement déclaratif ne prend en charge que le partitionnement par intervalle, par liste
et par hachage, tandis que l'héritage de table permet de diviser les données de la manière choisie
par l'utilisateur. (Notez, cependant, que si l'exclusion de contrainte n'est pas en mesure d'élaguer
efficacement les tables filles, la performance de la requête peut être faible).

• Certaines opérations nécessitent un verrou plus fort en utilisant le partitionnement déclaratif qu'en
utilisant l'héritage de table. Par exemple, ajouter ou supprimer une partition d'une table partitionnée
nécessite de prendre un verrou de type ACCESS EXCLUSIVE sur la table parente, alors qu'un
verrou de type SHARE UPDATE EXCLUSIVE est suffisant dans le cas de l'héritage classique.

5.10.3.1. Exemple
Cet exemple construit une structure de partitionnement équivalente à l'exemple de partitionnement
déclaratif ci-dessus. Procédez aux étapes suivantes :

1. Créez la table « master », de laquelle toutes les tables « filles » hériteront. Cette table ne contiendra
aucune donnée. Ne définissez aucune contrainte de vérification sur cette table, à moins que vous
n'ayez l'intention de l'appliquer de manière identique sur toutes les tables filles. Il n'y a aucun intérêt
à définir d'index ou de contrainte unique sur elle non plus. Pour notre exemple, la table master
correspond à la table measurement définie à l'origine :

CREATE TABLE measurement (


city_id int not null,
logdate date not null,
peaktemp int,
unitsales int

96
Définition des données

);
2. Créez plusieurs tables « enfant », chacune héritant de la table master. Normalement, ces tables
n'ajouteront aucune colonne à celles héritées de la table master. Comme avec le partitionnement
déclaratif, ces tables filles sont des tables PostgreSQL à part entière (ou des tables étrangères)
PostgreSQL normales.

CREATE TABLE measurement_y2006m02 () INHERITS (measurement);


CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
...
CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
CREATE TABLE measurement_y2008m01 () INHERITS (measurement);

3. Ajoutez les contraintes de tables, sans qu'elles se chevauchent, sur les tables filles pour définir les
valeurs de clé autorisées dans chacune.

Des exemples typiques seraient :

CHECK ( x = 1 )
CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire',
'Warwickshire' ))
CHECK ( outletID >= 100 AND outletID < 200 )

Assurez-vous que les contraintes garantissent qu'il n'y a pas de chevauchement entre les valeurs
de clés permises dans différentes tables filles. Une erreur fréquente est de mettre en place des
contraintes d'intervalle comme ceci :

CHECK ( outletID BETWEEN 100 AND 200 )


CHECK ( outletID BETWEEN 200 AND 300 )

Cet exemple est faux puisqu'on ne peut pas savoir à quelle table fille appartient la valeur de clé
200. À la place, les intervalles devraient être définis ainsi :

CREATE TABLE measurement_y2006m02 (


CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE
'2006-03-01' )
) INHERITS (measurement);

CREATE TABLE measurement_y2006m03 (


CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE
'2006-04-01' )
) INHERITS (measurement);

...
CREATE TABLE measurement_y2007m11 (
CHECK ( logdate >= DATE '2007-11-01' AND logdate < DATE
'2007-12-01' )
) INHERITS (measurement);

CREATE TABLE measurement_y2007m12 (


CHECK ( logdate >= DATE '2007-12-01' AND logdate < DATE
'2008-01-01' )
) INHERITS (measurement);

97
Définition des données

CREATE TABLE measurement_y2008m01 (


CHECK ( logdate >= DATE '2008-01-01' AND logdate < DATE
'2008-02-01' )
) INHERITS (measurement);

4. Pour chaque table fille, créez un index sur la ou les colonnes de la clé, ainsi que tout autre index
que vous voudriez.

CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02


(logdate);
CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03
(logdate);
CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11
(logdate);
CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12
(logdate);
CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01
(logdate);

5. Nous voulons que notre application soit capable de dire INSERT INTO measurement ...,
et de voir ses données redirigées dans la table fille appropriée. Nous pouvons réaliser cela en
ajoutant un trigger sur la table master. Si les données doivent être ajoutées sur la dernière table fille
uniquement, nous pouvons utiliser un trigger avec une fonction très simple :

CREATE OR REPLACE FUNCTION measurement_insert_trigger()


RETURNS TRIGGER AS $$
BEGIN
INSERT INTO measurement_y2008m01 VALUES (NEW.*);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;

Après avoir créé la fonction, nous créons le trigger qui appelle la fonction trigger :

CREATE TRIGGER insert_mesure_trigger


BEFORE INSERT ON mesure
FOR EACH ROW EXECUTE FUNCTION mesure_insert_trigger();

Une telle fonction doit être redéfinie chaque mois pour toujours insérer sur la table fille active. La
définition du trigger n'a pas besoin d'être redéfinie.

Il est également possible de laisser le serveur localiser la table fille dans laquelle doit être insérée
la ligne. Une fonction plus complexe peut alors être utilisée :

CREATE OR REPLACE FUNCTION mesure_insert_trigger()


RETURNS TRIGGER AS $$
BEGIN
IF ( NEW.date_trace >= DATE '2006-02-01' AND
NEW.date_trace < DATE '2006-03-01' ) THEN
INSERT INTO mesure_a2006m02 VALUES (NEW.*);
ELSIF ( NEW.date_trace >= DATE '2006-03-01' AND

98
Définition des données

NEW.date_trace < DATE '2006-04-01' ) THEN


INSERT INTO mesure_a2006m03 VALUES (NEW.*);
...
ELSIF ( NEW.date_trace >= DATE '2008-01-01' AND
NEW.date_trace < DATE '2008-02-01' ) THEN
INSERT INTO mesure_a2008m01 VALUES (NEW.*);
ELSE
RAISE EXCEPTION 'Date en dehors de l''intervalle.
Corrigez la fonction mesure_insert_trigger() !';
END IF;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;

La définition du trigger est la même qu'avant. Notez que chaque test IF doit correspondre
exactement à la contrainte CHECK de la table fille correspondante.

Bien que cette fonction soit plus complexe que celle pour un seul mois, il n'est pas nécessaire de
l'actualiser aussi fréquemment, les branches pouvant être ajoutées en avance.

Note
En pratique, il vaudrait mieux vérifier d'abord la dernière table fille créée si la plupart des
insertions lui sont destinées. Pour des raisons de simplicité, les tests du trigger sont présentés
dans le même ordre que les autres parties de l'exemple.

Une approche différente du trigger est la redirection des insertions par des règles sur la table master.
Par exemple :

CREATE RULE measurement_insert_y2006m02 AS


ON INSERT TO measurement WHERE
( logdate >= DATE '2006-02-01' AND logdate < DATE
'2006-03-01' )
DO INSTEAD
INSERT INTO measurement_y2006m02 VALUES (NEW.*);
...
CREATE RULE measurement_insert_y2008m01 AS
ON INSERT TO measurement WHERE
( logdate >= DATE '2008-01-01' AND logdate < DATE
'2008-02-01' )
DO INSTEAD
INSERT INTO measurement_y2008m01 VALUES (NEW.*);

Une règle a un surcoût bien plus important qu'un trigger, mais il n'est payé qu'une fois par requête
plutôt qu'une fois par ligne. Cette méthode peut donc être avantageuse pour les insertions en masse.
Toutefois, dans la plupart des cas, la méthode du trigger offrira de meilleures performances.

Soyez conscient que COPY ignore les règles. Si vous voulez utiliser COPY pour insérer des données,
vous devrez les copier dans la bonne table fille plutôt que dans la table master. COPY déclenche les
triggers, vous pouvez donc l'utiliser normalement si vous utilisez l'approche par trigger.

99
Définition des données

Un autre inconvénient à l'approche par règle est qu'il n'y a pas de moyen simple de forcer une erreur
si l'ensemble de règles ne couvre pas la date d'insertion ; les données iront silencieusement dans
la table master à la place.
6. Assurez-vous que le paramètre de configuration constraint_exclusion ne soit pas désactivé dans
postgresql.conf ; sinon il pourrait y avoir des accès inutiles aux autres tables.

Comme nous pouvons le voir, une hiérarchie complexe de tables peut nécessiter une quantité de DDL
non négligeable. Dans l'exemple ci-dessus, nous créerions une nouvelle table fille chaque mois, il
serait donc sage d'écrire un script qui génère le DDL automatiquement.

5.10.3.2. Maintenance du partitionnement par héritage


Pour supprimer les anciennes données rapidement, il suffit de supprimer la table fille qui n'est plus
nécessaire :

DROP TABLE mesure_a2006m02;

Pour enlever une table fille de la hiérarchie d'héritage, mais en en gardant l'accès en tant que table
normale :

ALTER TABLE mesure_a2006m02 NO INHERIT mesure;

Pour ajouter une nouvelle table fille pour gérer les nouvelles données, créez une table fille vide, tout
comme les tables filles originales ont été créées ci-dessus :

CREATE TABLE mesure_a2008m02 (


CHECK ( date_trace >= DATE '2008-02-01' AND date_trace < DATE
'2008-03-01' )
) INHERITS (mesure);

Une autre alternative est de créer et de remplir la nouvelle table enfant avant de l'ajouter à la hiérarchie
de la table. Ceci permet aux données d'être chargées, vérifiées et transformées avant d'être rendues
visibles aux requêtes sur la table parente.

CREATE TABLE mesure_a2008m02


(LIKE mesure INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
ALTER TABLE mesure_a2008m02 ADD CONSTRAINT y2008m02
CHECK ( date_trace >= DATE '2008-02-01' AND date_trace < DATE
'2008-03-01' );
\copy mesure_a2008m02 from 'mesure_a2008m02'
-- quelques travaux de préparation des données
ALTER TABLE mesure_a2008m02 INHERIT mesure;

5.10.3.3. Restrictions
Les restrictions suivantes s'appliquent au partitionnement par héritage :

• Il n'existe pas de moyen automatique de vérifier que toutes les contraintes de vérification (CHECK)
sont mutuellement exclusives. Il est plus sûr de créer un code qui fabrique les tables filles, et crée
et/ou modifie les objets associés plutôt que de les créer manuellement ;

• les schémas montrés ici supposent que les colonnes clés du partitionnement d'une ligne ne changent
jamais ou, tout du moins, ne changent pas suffisamment pour nécessiter un déplacement vers une

100
Définition des données

autre partition. Une commande UPDATE qui tentera de le faire échouera à cause des contraintes
CHECK. Si vous devez gérer ce type de cas, des triggers sur mise à jour peuvent être placés sur les
tables filles, mais cela rend la gestion de la structure beaucoup plus complexe.

• Si VACUUM ou ANALYZE sont lancés manuellement, n'oubliez pas de les lancer sur chaque table
fille. Une commande comme :

ANALYZE mesure;

ne traitera que la table maître.

• Les commandes INSERT avec des clauses ON CONFLICT ont peu de chances de fonctionner
comme attendu, puisque l'action du ON CONFLICT n'est effectuée que dans le cas de violations
d'unicité dans la table cible, pas dans les filles.

• Des triggers ou des règles seront nécessaires pour rediriger les lignes vers la table fille voulue, à
moins que l'application ne soit explicitement au courant du schéma de partitionnement. Les triggers
peuvent être compliqués à écrire, et seront bien plus lents que la redirection de ligne effectuée en
interne par le partitionnement déclaratif.

5.10.4. Élagage de partition


L'élagage des partitions (Partition pruning) est une technique d'optimisation des requêtes qui
vise à améliorer les performances des tables à partitionnement déclaratif. À titre d'exemple :

SET enable_partition_pruning = on; -- défaut


SELECT count(*) FROM mesure WHERE date_trace >= DATE '2008-01-01';

Sans l'élagage de partition, la requête ci-dessus parcourrait chacune des partitions de la table mesure.
Avec l'élagage de partition activé, le planificateur examinera la définition de chaque partition, et
montrera qu'il n'est pas nécessaire de la parcourir puisqu'elle ne contient aucune ligne respectant la
clause WHERE de la requête. Lorsque le planificateur peut l'établir, il exclut (élague) la partition du
plan de recherche.

En utilisant la commande EXPLAIN et le paramètre de configuration enable_partition_pruning, il est


possible de voir la différence entre un plan pour lequel des partitions ont été élaguées et celui pour
lequel elles ne l'ont pas été. Un plan typique non optimisé pour ce type de configuration de table serait :

SET enable_partition_pruning = off;


EXPLAIN SELECT count(*) FROM mesure WHERE date_trace >= DATE
'2008-01-01';

QUERY PLAN
--------------------------------------------------------------------------------
Aggregate (cost=188.76..188.77 rows=1 width=8)
-> Append (cost=0.00..181.05 rows=3085 width=0)
-> Seq Scan on measurement_y2006m02 (cost=0.00..33.12
rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)
-> Seq Scan on measurement_y2006m03 (cost=0.00..33.12
rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)
...
-> Seq Scan on measurement_y2007m11 (cost=0.00..33.12
rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)

101
Définition des données

-> Seq Scan on measurement_y2007m12 (cost=0.00..33.12


rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)
-> Seq Scan on measurement_y2008m01 (cost=0.00..33.12
rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)

Quelques partitions, voire toutes, peuvent utiliser des parcours d'index à la place des parcours
séquentiels de la table complète, mais le fait est qu'il n'est pas besoin de parcourir les plus vieilles
partitions pour répondre à cette requête. Lorsque l'élagage de partitions est activé, nous obtenons un
plan significativement moins coûteux, pour le même résultat :

SET enable_partition_pruning = on;


EXPLAIN SELECT count(*) FROM mesure WHERE date_trace >= DATE
'2008-01-01';

QUERY PLAN
--------------------------------------------------------------------------------
Aggregate (cost=37.75..37.76 rows=1 width=8)
-> Append (cost=0.00..36.21 rows=617 width=0)
-> Seq Scan on measurement_y2008m01 (cost=0.00..33.12
rows=617 width=0)

Il est à noter que l'élagage des partitions n'est piloté que par les contraintes définies implicitement
par les clés de partition, et non par la présence d'index : il n'est donc pas nécessaire de définir des
index sur les colonnes clés. Si un index doit être créé pour une partition donnée, ceci dépendra du fait
que vous vous attendez à ce que les requêtes qui parcourent la partition parcourent généralement une
grande partie de la partition ou seulement une petite partie. Un index sera utile dans ce dernier cas,
mais pas dans le premier.

L'élagage des partitions peut être effectuée non seulement lors de la planification d'une requête, mais
aussi lors de son exécution. Ceci est utile car cela peut permettre d'élaguer plus de partitions lorsque les
clauses contiennent des expressions dont les valeurs ne sont pas connues au moment de la planification
de la requête ; par exemple, des paramètres définis dans une instruction PREPARE, utilisant une valeur
obtenue d'une sous-requête ou utilisant une valeur paramétrée sur la partie interne du nœud de boucle
imbriqué (nested loop join. L'élagage de la partition pendant l'exécution peut être réalisé à
l'un des moments suivant :

• Lors de l'initialisation du plan d'exécution, l'élagage de partition peut être effectué pour les valeurs
de paramètres qui sont connues pendant cette phase. Les partitions qui ont été élaguées pendant
cette étape n'apparaîtront pas dans l'EXPLAIN ou l'EXPLAIN ANALYZE de la requête. Il est tout
de même possible de déterminer le nombre de partitions qui ont été supprimées pendant cette phase
en observant la propriété « Subplans Removed » (sous-plans supprimés) dans la sortie d'EXPLAIN.

• Pendant l'exécution effective du plan d'exécution. L'élagage des partitions peut également être
effectué pour supprimer des partitions en utilisant des valeurs qui ne sont connues que pendant
l'exécution de la requête. Cela inclut les valeurs des sous-requêtes et des paramètres issus de
l'exécution, comme des jointures par boucle imbriquée (nested loop join) paramétrées.
Comme la valeur de ces paramètres peut changer plusieurs fois pendant l'exécution de la requête,
l'élagage de la partition est effectué chaque fois que l'un des paramètres d'exécution utilisés pour
celui-ci change. Déterminer si les partitions ont été élaguées pendant cette phase nécessite une
inspection minutieuse de la propriété nloops de la sortie d'EXPLAIN ANALYZE. Les sous-plans
correspondant aux différentes partitions pourraient avoir différentes valeurs dépendant du nombre
de fois chacun d'entre eux a été évité lors de l'exécution. Certains pourraient être affichés comme
(never executed) (littéralement, jamais exécuté) s'ils sont évités à chaque fois.

L'élagage des partitions peut être désactivé à l'aide du paramètre enable_partition_pruning.

102
Définition des données

Note
L'élagage de partitions au moment de l'exécution survient seulement pour le type de nœud
Append, mais pas pour les nœuds MergeAppend et ModifyTable. Ceci pourrait changer
dans une prochaine version de PostgreSQL.

5.10.5. Partitionnement et Contrainte d'exclusion


Une contrainte d'exclusion est une technique d'optimisation de requêtes similaire à l'élagage de
partitions. Bien qu'elle soit principalement utilisé pour les tables partitionnées avec l'ancienne méthode
par héritage, elle peut être utilisée à d'autres fins, y compris avec le partitionnement déclaratif.

Les contraintes d'exclusion fonctionnent d'une manière très similaire à l'élagage de partitions, sauf
qu'elles utilisent les contraintes CHECK de chaque table (d'où le nom) alors que l'élagage de partition
utilise les limites de partition de la table, qui n'existent que dans le cas d'un partitionnement déclaratif.
Une autre différence est qu'une contrainte d'exclusion n'est appliquée qu'à la planification ; il n'y a
donc pas de tentative d'écarter des partitions dès l'exécution.

Le fait que les contraintes d'exclusion utilisent les contraintes CHECK les rend plus lentes que l'élagage
de partitions, mais peut être un avantage : puisque les contraintes peuvent être définies même sur des
tables avec partitionnement déclaratif, en plus de leurs limites internes, les contraintes d'exclusion
peuvent être capables de supprimer des partitions supplémentaires pendant la phase de planification
de la requête.

La valeur par défaut (et donc recommandée) de constraint_exclusion n'est ni on ni off, mais un
état intermédiaire appelé partition, qui fait que la technique n'est appliquée qu'aux requêtes
qui semblent fonctionner avec des tables partitionnées par héritage. La valeur on entraîne que le
planificateur examine les contraintes CHECK dans toutes les requêtes, y compris les requêtes simples
qui ont peu de chance d'en profiter.

Les restrictions suivantes s'appliquent à l'exclusion de contraintes :

• Les contraintes d'exclusion ne sont appliquées que lors de la phase de planification de la requête,
contrairement à l'élagage de partition, qui peut être appliqué lors de la phase d'exécution.

• La contrainte d'exclusion ne fonctionne que si la clause WHERE de la requête contient des constantes
(ou des paramètres externes). Par exemple, une comparaison avec une fonction non immutable
comme CURRENT_TIMESTAMP ne peut pas être optimisée, car le planificateur ne peut pas savoir
dans quelle table fille la valeur de la fonction ira lors de l'exécution.

• Les contraintes de partitionnement doivent rester simples. Dans le cas contraire, le planificateur
peut rencontrer des difficultés à déterminer les tables filles qu'il n'est pas nécessaire de parcourir.
Des conditions simples d'égalité pour le partitionnement de liste, ou des tests d'intervalle simples
lors de partitionnement par intervalles sont recommandées, comme illustré dans les exemples
précédents. Une règle générale est que les contraintes de partitionnement ne doivent contenir que
des comparaisons entre les colonnes partitionnées et des constantes, à l'aide d'opérateurs utilisables
par les index B-tree, car seules les colonnes indexables avec un index B-tree sont autorisées dans
la clé de partitionnement.

• Toutes les contraintes sur toutes les filles de la table parente sont examinées lors de l'exclusion
de contraintes. De ce fait, un grand nombre de filles augmente considérablement le temps de
planification de la requête. Ainsi, l'ancien partitionnement par héritage fonctionnera bien jusqu'à,
peut-être, une centaine de tables enfant ; n'essayez pas d'en utiliser plusieurs milliers.

103
Définition des données

5.10.6. Bonnes pratiques pour le partitionnement


déclaratif
Le choix de la méthode de partitionnement d'une table doit être fait avec beaucoup d'attention car
les performances de l'optimisation des requêtes et leur exécution peuvent être fortement affectées
négativement par un mauvais design.

Une des décisions les plus critiques au niveau du design est le choix de la clé (ou des clés) de
partitionnement. Souvent, le meilleur choix revient à partitionner par la (ou les) colonne(s) qui
apparaissent le plus fréquemment dans les clauses WHERE des requêtes en cours d'exécution sur la table
partitionnée. Les éléments de la clause WHERE qui sont compatibles avec les contraintes des limites
des partitions peuvent être utilisés pour ignorer les partitions inutiles. La suppression des données
inutiles est aussi un facteur à considérer lors de la conception de votre stratégie de partitionnement. Une
partition entière peut être détachée rapidement, donc il peut être bénéfique de concevoir la stratégie
de partitionnement d'une telle façon que tout les données à supprimer d'un coup soient concentrées
sur une seule partition.

Choisir le nombre cible de partitions pour la table est aussi une décision critique à prendre. Ne pas avoir
suffisamment de partitions pourrait avoir pour conséquence des index trop gros, et un emplacement des
données pauvre qui résulterait en un ratio bas de lecture en cache. Néanmoins, diviser la table en trop
de partitions pourrait aussi causer des problèmes. Trop de partitions pourrait signifier une optimisation
plus longue des requêtes et une consommation mémoire plus importante durant l'optimisation et
l'exécution, comme indiqué plus bas. Lors de la conception du partitionnement de votre table, il est
aussi important de prendre compte les changements pouvant survenir dans le futur. Par exemple, si
vous choisissez d'avoir une partition par client et que vous avez un petit nombre de gros clients, il
est important de réfléchir aux implications si, dans quelques années, vous vous trouvez avec un grand
nombre de petits clients. Dans ce cas, il serait mieux de choisir de partitionner par RANGE et de choisir
un nombre raisonnable de partitions, chacune contenant un nombre fixe de clients, plutôt que d'essayer
de partitionner par LIST en espérant que le nombre de clients ne dépasse pas ce qui est possible au
niveau du partitionnement des données.

Le sous-partitionnement peut aussi être utile pour diviser encore plus les partitions qui pourraient
devenir plus grosses que les autres partitions. Une autre option est d'utiliser le partitionnement par
intervalle avec plusieurs colonnes dans la clé de partitionnement. Chacune de ses solutions peut
facilement amener à un nombre excessif de partitions, il convient donc de rester prudent.

Il est important de considérer la surcharge occasionné par le partitionnement lors de l'optimisation


et de l'exécution. L'optimiseur est généralement capble de gérer les hiérarchies de partitions qui
montent à quelques centaines de partitions. Les durées d'optimisation deviennent plus longues et la
consommation de mémoire devient plus importante au fur et à mesure de l'ajout de partitions. Ceci est
tout particulièrement vrai pour les commandes UPDATE et DELETE. Une autre raison de se soucier
d'un grand nombre de partitions est que la consommation mémoire du serveur pourrait grossir de façon
significative sur une période de temps, et tout spécialement si beaucoup de sessions touchent un grand
nombre de partitions. Ceci est dû au chargement des métadonnées nécessaires pour chaque partition
en mémoire locale.

Avec une charge de type entrepôt de données, il peut être sensé d'utiliser un plus grand nombre de
partitions que pour une charge de type OLTP. En général, dans les entrepôts de données, le temps
d'optimisation d'une requête est peu importante parce que la majorité du temps de traitement est passée
sur l'exécution de la requête. Avec l'une de ces deux types de charges, il est important de prendre
les bonnes décisions dès le début, car le re-partitionnement de grosses quantités de données peut être
très lent. Les simulations de la charge attendue sont souvent bénéfiques pour optimiser la stratégie de
partitionnement. Ne jamais supposer qu'un plus grand nombre de partitions est toujours mieux qu'un
petit nombre de partitions, et vice-versa.

5.11. Données distantes


104
Définition des données

PostgreSQL implémente des portions de la norme SQL/MED, vous permettant d'accéder à des données
qui résident en dehors de PostgreSQL en utilisant des requêtes SQL standards. On utilise le terme de
données distantes pour de telles données. (Notez qu'en anglais il y a ambiguïté : les données distantes
(foreign data) n'ont rien à voir avec les clés étrangères (foreign keys), qui sont un type de contrainte
à l'intérieur de la base de données.)

Les données distantes sont accédées grâce à un wrapper de données distantes. Ce dernier est une
bibliothèque qui peut communiquer avec une source de données externe, cachant les détails de la
connexion vers la source de données et de la récupération des données à partir de cette source. Il
existe des wrappers de données distantes disponibles en tant que modules contrib. D'autres types de
wrappers de données distantes peuvent faire partie de produits tiers. Si aucun des wrappers de données
distantes ne vous convient, vous pouvez écrire le vôtre. Voir Chapitre 57.

Pour accéder aux données distantes, vous devez créer un objet de type serveur distant qui définit la
façon de se connecter à une source de données externes particulière suivant un ensemble d'options
utilisées par un wrapper de données distantes. Ensuite, vous aurez besoin de créer une ou plusieurs
tables distantes, qui définissent la structure des données distantes. Une table distante peut être utilisée
dans des requêtes comme toute autre table, mais une table distante n'est pas stockée sur le serveur
PostgreSQL. À chaque utilisation, PostgreSQL demande au wrapper de données distantes de récupérer
les données provenant de la source externe, ou de transmettre les données à la source externe dans le
cas de commandes de mise à jour.

Accéder à des données distantes pourrait nécessiter une authentification auprès de la source de données
externes. Cette information peut être passée par une correspondance d'utilisateur, qui peut fournir des
données comme les noms d'utilisateurs et mots de passe en se basant sur le rôle PostgreSQL actuel.

Pour plus d'informations, voir CREATE FOREIGN DATA WRAPPER, CREATE SERVER,
CREATE USER MAPPING, CREATE FOREIGN TABLE et IMPORT FOREIGN SCHEMA.

5.12. Autres objets de la base de données


Les tables sont les objets centraux dans une structure de base de données relationnelle, car ce sont elles
qui stockent les données. Mais ce ne sont pas les seuls objets qui existent dans une base de données. De
nombreux autres types d'objets peuvent être créés afin de rendre l'utilisation et la gestion des données
plus efficace ou pratique. Ils ne sont pas abordés dans ce chapitre, mais une liste en est dressée à titre
d'information.

• Vues

• Fonctions, procédures et opérateurs

• Types de données et domaines

• Triggers et règles de réécriture

Des informations détaillées sur ces sujets apparaissent dans la Partie V.

5.13. Gestion des dépendances


Lorsque des structures de base complexes sont créées qui impliquent beaucoup de tables avec des
contraintes de clés étrangères, des vues, des triggers, des fonctions, etc., un réseau de dépendances
entre les objets est implicitement créé. Par exemple, une table avec une contrainte de clé étrangère
dépend de la table à laquelle elle fait référence.

Pour garantir l'intégrité de la structure entière de la base, PostgreSQL s'assure qu'un objet dont d'autres
objets dépendent ne peut pas être supprimé. Ainsi, toute tentative de suppression de la table des
produits utilisée dans la Section 5.3.5, sachant que la table des commandes en dépend, lève un message
d'erreur comme celui-ci :

105
Définition des données

DROP TABLE produits;

ERROR: cannot drop table produits because other objects depend on


it
DETAIL: constraint commandes_no_produit_fkey on table commandes
depends on table produits
HINT: Use DROP ... CASCADE to drop the dependent objects too.

ou en français :

DROP TABLE produits;

NOTICE: la contrainte commandes_no_produit_fkey sur la table


commandes dépend
de la table produits
ERREUR: la table produits ne peut pas être supprimée, car d'autres
objets en
dépendent
HINT: Utiliser DROP ... CASCADE pour supprimer également les
objets
dépendants.

Le message d'erreur contient un indice utile : pour ne pas avoir à supprimer individuellement chaque
objet dépendant, on peut lancer

DROP TABLE produits CASCADE;

et tous les objets dépendants sont ainsi effacés, comme tous les objets dépendant de ces derniers,
récursivement. Dans ce cas, la table des commandes n'est pas supprimée, mais seulement la contrainte
de clé étrangère. Elle s'arrête là, car rien ne dépend d'une contrainte de clé étrangère. (Pour vérifier ce
que fait DROP ... CASCADE, on peut lancer DROP sans CASCADE et lire les messages DETAIL.)

Pratiquement toutes les commandes DROP dans PostgreSQL supportent l'utilisation de CASCADE.
La nature des dépendances est évidemment fonction de la nature des objets. On peut aussi écrire
RESTRICT au lieu de CASCADE pour obtenir le comportement par défaut, à savoir interdire les
suppressions d'objets dont dépendent d'autres objets.

Note
D'après le standard SQL, il est nécessaire d'indiquer RESTRICT ou CASCADE dans une
commande DROP. Aucun système de base de données ne force cette règle, en réalité, mais le
choix du comportement par défaut, RESTRICT ou CASCADE, varie suivant le système.

Si une commande DROP liste plusieurs objets, CASCADE est seulement requis quand il existe des
dépendances en dehors du groupe spécifié. Par exemple, en indiquant DROP TABLE tab1, tab2,
l'existence d'une clé étrangère référençant tab1 à partir de tab2 ne signifie pas que CASCADE est
nécessaire pour réussir.

Pour les fonctions définies par les utilisateurs, PostgreSQL trace les dépendances associées avec les
propriétés de la fonction visibles en externe, comme les types de données des arguments et du résultat.
Par contre, il ne trace pas les dépendances seulement connues en examinant le corps de la fonction.
Par exemple :

CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow',


'green', 'blue', 'purple');

CREATE TABLE my_colors (color rainbow, note text);

106
Définition des données

CREATE FUNCTION get_color_note (rainbow) RETURNS text AS


'SELECT note FROM my_colors WHERE color = $1'
LANGUAGE SQL;

(Voir Section 38.5 pour une explication sur les fonctions en SQL.) PostgreSQL aura connaissance du
fait que la fonction get_color_note dépend du type rainbow : supprimer ce type de données
forcera la suppression de la fonction parce que le type de son argument ne serait plus défini. Mais
PostgreSQL ne considérera pas que la fonction get_color_note dépende de la table my_colors,
et donc ne supprimera pas la fonction si la table est supprimée. Bien qu'il y ait des inconvénients à
cette approche, il y a aussi des avantages. La fonction est toujours valide d'une certaine façon si la
table est manquante, bien que son exécution causera une erreur. Créer une nouvelle table de même
nom permettra à la fonction d'être valide de nouveau.

107
Chapitre 6. Manipulation de données
Le chapitre précédent présente la création des tables et des autres structures de stockage des données.
Il est temps de remplir ces tables avec des données. Le présent chapitre couvre l'insertion, la mise à
jour et la suppression des données des tables. Après cela, le chapitre présente l'élimination des données
perdues.

6.1. Insérer des données


Quand une table est créée, elle ne contient aucune donnée. La première chose à faire, c'est d'y
insérer des données. Sans quoi la base de données n'est pas d'une grande utilité. Les données sont
conceptuellement insérées ligne par ligne. Il est évidemment possible d'insérer plus d'une ligne, mais il
n'est pas possible d'entrer moins d'une ligne. Même lorsque seules les valeurs d'une partie des colonnes
sont connues, une ligne complète doit être créée.

Pour créer une nouvelle ligne, la commande INSERT est utilisée. La commande a besoin du nom de
la table et des valeurs de colonnes.

Soit la table des produits du Chapitre 5 :

CREATE TABLE produits (


no_produit integer,
nom text,
prix numeric
);

Une commande d'insertion d'une ligne peut être :

INSERT INTO produits VALUES (1, 'Fromage', 9.99);

Les données sont listées dans l'ordre des colonnes de la table, séparées par des virgules. Souvent, les
données sont des libellés (constantes), mais les expressions scalaires sont aussi acceptées.

La syntaxe précédente oblige à connaître l'ordre des colonnes. Pour éviter cela, les colonnes peuvent
être explicitement listées. Les deux commandes suivantes ont, ainsi, le même effet que la précédente :

INSERT INTO produits (no_produit, nom, prix) VALUES (1, 'Fromage',


9.99);
INSERT INTO produits (nom, prix, no_produit) VALUES ('Fromage',
9.99, 1);

Beaucoup d'utilisateurs recommandent de toujours lister les noms de colonnes.

Si les valeurs de certaines colonnes ne sont pas connues, elles peuvent être omises. Dans ce cas, elles
sont remplies avec leur valeur par défaut. Par exemple :

INSERT INTO produits (no_produit, nom) VALUES (1, 'Fromage');


INSERT INTO produits VALUES (1, 'Fromage');

La seconde instruction est une extension PostgreSQL. Elle remplit les colonnes de gauche à droite
avec toutes les valeurs données, et les autres prennent leur valeur par défaut.

Il est possible, pour plus de clarté, d'appeler explicitement les valeurs par défaut pour des colonnes
particulières ou pour la ligne complète.

INSERT INTO produits (no_produit, nom, prix) VALUES (1, 'Fromage',


DEFAULT);
INSERT INTO produits DEFAULT VALUES;

108
Manipulation de données

Plusieurs lignes peuvent être insérées en une seule commande :

INSERT INTO produits (no_produit, nom, prix) VALUES


(1, 'Fromage', 9.99),
(2, 'Pain', 1.99),
(3, 'Lait', 2.99);

Il est aussi possible d'insérer le résultat d'une requête (qui pourrait renvoyer aucune ligne, une ligne
ou plusieurs lignes) :

INSERT INTO produits (no_produit, nom, prix)


SELECT no_produit, nom, prix FROM nouveaux_produits
WHERE date_sortie = 'today';

Ceci montre la grande puissance du mécanisme des requêtes SQL (Chapitre 7) sur le traitement des
lignes à insérer.

Astuce
Lors de l'insertion d'une grande quantité de données en même temps, il est préférable d'utiliser
la commande COPY. Elle n'est pas aussi flexible que la commande INSERT, mais elle est plus
efficace. Se référer à Section 14.4 pour plus d'informations sur l'amélioration des performances
lors de gros chargements de données.

6.2. Actualiser les données


La modification de données présentes en base est appelée mise à jour ou actualisation (update en
anglais). Il est possible de mettre à jour une ligne spécifique, toutes les lignes ou un sous-ensemble
de lignes de la table. Chaque colonne peut être actualisée séparément ; les autres colonnes ne sont
alors pas modifiées.

Pour mettre à jour les lignes existantes, utilisez la commande UPDATE. Trois informations sont
nécessaires :

1. le nom de la table et de la colonne à mettre à jour ;


2. la nouvelle valeur de la colonne ;
3. les lignes à mettre à jour.

Comme cela a été vu dans le Chapitre 5, le SQL ne donne pas, par défaut, d'identifiant unique pour
les lignes. Il n'est, de ce fait, pas toujours possible d'indiquer directement la ligne à mettre à jour.
On précise plutôt les conditions qu'une ligne doit remplir pour être mise à jour. Si la table possède
une clé primaire (qu'elle soit déclarée ou non), une ligne unique peut être choisie en précisant une
condition sur la clé primaire. Les outils graphiques d'accès aux bases de données utilisent ce principe
pour permettre les modifications de lignes individuelles.

La commande suivante, par exemple, modifie tous les produits dont le prix est 5 en le passant à 10.

UPDATE produits SET prix = 10 WHERE prix = 5;

Cela peut mettre à jour zéro, une, ou plusieurs lignes. L'exécution d'une commande UPDATE qui ne
met à jour aucune ligne ne représente pas une erreur.

Dans le détail de la commande, on trouve tout d'abord, le mot-clé UPDATE suivi du nom de la table.
Le nom de la table peut toujours être préfixé par un nom de schéma ; dans le cas contraire, elle est

109
Manipulation de données

recherchée dans le chemin. On trouve ensuite le mot-clé SET suivi du nom de la colonne, un signe
égal et la nouvelle valeur de la colonne, qui peut être une constante ou une expression scalaire.

Par exemple, pour augmenter de 10% le prix de tous les produits, on peut exécuter :

UPDATE produits SET prix = prix * 1.10;

L'expression donnant la nouvelle valeur peut faire référence aux valeurs courantes de la ligne.

Il n'a pas été indiqué ici de clause WHERE. Si elle est omise, toutes les lignes de la table sont modifiées.
Si elle est présente, seules les lignes qui remplissent la condition WHERE sont mises à jour. Le signe
égal dans la clause SET réalise une affectation, alors que celui de la clause WHERE permet une
comparaison. Pour autant, cela ne crée pas d'ambiguïté. La condition WHERE n'est pas nécessairement
un test d'égalité ; de nombreux autres opérateurs existent (voir le Chapitre 9). Mais le résultat de
l'expression est booléen.

Il est possible d'actualiser plusieurs colonnes en une seule commande UPDATE par l'indication de
plusieurs colonnes dans la clause SET.

Par exemple :

UPDATE ma_table SET a = 5, b = 3, c = 1 WHERE a > 0;

6.3. Supprimer des données


Les parties précédentes présentent l'ajout et la modification de données. Il reste à voir leur suppression
quand elles ne sont plus nécessaires. Comme pour l'insertion, la suppression ne peut se faire que par
ligne entière. Le SQL ne propose pas de moyen d'accéder à une ligne particulière. C'est pourquoi la
suppression de lignes se fait en indiquant les conditions à remplir par les lignes à supprimer. S'il y a
une clé primaire dans la table, alors il est possible d'indiquer précisément la ligne à supprimer. Mais
on peut aussi supprimer un groupe de lignes qui remplissent une condition, ou même toutes les lignes
d'une table en une fois.

Pour supprimer des lignes, on utilise la commande DELETE ; la syntaxe est très similaire à la
commande UPDATE.

Par exemple, pour supprimer toutes les lignes de la table produits qui ont un prix de 10, on exécute :

DELETE FROM produits WHERE prix = 10;

En indiquant simplement :

DELETE FROM produits;

on supprime toutes les lignes de la table. Attention aux mauvaises manipulations !

6.4. Renvoyer des données provenant de


lignes modifiées
Parfois, il est intéressant d'obtenir des données de lignes modifiées pendant qu'elles sont manipulées.
Les commandes INSERT, UPDATE et DELETE ont toutes une clause RETURNING optionnelle qui
le permet. L'utilisation de la clause RETURNING évite l'exécution d'une requête supplémentaire pour
coller les données, et est particulièrement intéressante quand il serait difficile d'identifier autrement
les lignes modifiées.

Le contenu autorisé d'une clause RETURNING est identique à celui de la liste de sortie d'une commande
SELECT (voir Section 7.3). Elle peut contenir les noms des colonnes de la table cible ou des

110
Manipulation de données

expressions utilisant ces colonnes. Un raccourci habituel est RETURNING *, qui sélectionne toutes
les colonnes de la table cible, dans l'ordre de définition.

Avec un INSERT, les données disponibles à RETURNING sont la ligne qui a été insérée. Ceci n'est
pas utile pour les insertions simples, car cela ne fera que répéter les données fournies par le client, mais
cela peut devenir très utile si la commande se base sur les valeurs calculées par défaut. Par exemple,
lors de l'utilisation d'une colonne serial fournissant des identifiants uniques, RETURNING peut
renvoyer l'identifiant affecté à une nouvelle ligne :

CREATE TABLE utilisateurs (prenom text, nom text, id serial primary


key);

INSERT INTO utilisateurs (prenom, nom) VALUES ('Joe', 'Cool')


RETURNING id;

La clause RETURNING est aussi très utile avec un INSERT ... SELECT

Dans un UPDATE, les données disponibles pour la clause RETURNING correspondent au nouveau
contenu de la ligne modifiée. Par exemple :

UPDATE produits SET prix = prix * 1.10


WHERE prix <= 99.99
RETURNING nom, prix AS nouveau_prix;

Dans un DELETE, les données disponibles pour la clause RETURNING correspondent au contenu de
la ligne supprimée. Par exemple :

DELETE FROM produits


WHERE date_perime = 'today'
RETURNING *;

Si des triggers (Chapitre 39) sont définis sur la table cible, les données disponibles pour la clause
RETURNING correspondent à la ligne modifiée par les triggers. De ce fait, une utilisation courante de
la clause RETURNING est d'inspecter les colonnes calculées par les triggers.

111
Chapitre 7. Requêtes
Les précédents chapitres ont expliqué comme créer des tables, comment les remplir avec des données
et comment manipuler ces données. Maintenant, nous discutons enfin de la façon de récupérer ces
données depuis la base de données.

7.1. Aperçu
Le processus et la commande de récupération des données sont appelés une requête. En SQL, la
commande SELECT est utilisée pour spécifier des requêtes. La syntaxe générale de la commande
SELECT est

[WITH with_requêtes] SELECT liste_select FROM expression_table


[specification_tri]

Les sections suivantes décrivent le détail de la liste de sélection, l'expression des tables et la
spécification du tri. Les requêtes WITH sont traitées en dernier, car il s'agit d'une fonctionnalité
avancée.

Un type de requête simple est de la forme :

SELECT * FROM table1;

En supposant qu'il existe une table appelée table1, cette commande récupérera toutes les lignes
et toutes les colonnes, définies par l'utilisateur, de table1. La méthode de récupération dépend de
l'application cliente. Par exemple, le programme psql affichera une table, façon art ASCII, alors que les
bibliothèques du client offriront des fonctions d'extraction de valeurs individuelles à partir du résultat
de la requête. * comme liste de sélection signifie que toutes les colonnes de l'expression de table
seront récupérées. Une liste de sélection peut aussi être un sous-ensemble des colonnes disponibles ou
effectuer un calcul en utilisant les colonnes. Par exemple, si table1 dispose des colonnes nommées
a, b et c (et peut-être d'autres), vous pouvez lancer la requête suivante :

SELECT a, b + c FROM table1;

(en supposant que b et c soient de type numérique). Voir la Section 7.3 pour plus de détails.

FROM table1 est un type très simple d'expression de tables : il lit une seule table. En général,
les expressions de tables sont des constructions complexes de tables de base, de jointures et de sous-
requêtes. Mais vous pouvez aussi entièrement omettre l'expression de table et utiliser la commande
SELECT comme une calculatrice :

SELECT 3 * 4;

Ceci est plus utile si les expressions de la liste de sélection renvoient des résultats variants. Par
exemple, vous pouvez appeler une fonction de cette façon :

SELECT random();

7.2. Expressions de table


Une expression de table calcule une table. L'expression de table contient une clause FROM qui peut être
suivie des clauses WHERE, GROUP BY et HAVING. Les expressions triviales de table font simplement
référence à une table sur le disque, une table de base, mais des expressions plus complexes peuvent
être utilisées pour modifier ou combiner des tables de base de différentes façons.

112
Requêtes

Les clauses optionnelles WHERE, GROUP BY et HAVING dans l'expression de table spécifient un
tube de transformations successives réalisées sur la table dérivée de la clause FROM. Toutes ces
transformations produisent une table virtuelle fournissant les lignes à passer à la liste de sélection qui
choisira les lignes à afficher de la requête.

7.2.1. Clause FROM


La la section intitulée « Clause FROM » dérive une table à partir d'une ou plusieurs tables données dans
une liste de référence dont les tables sont séparées par des virgules.

FROM reference_table [, reference_table [, ...]]

Une référence de table pourrait être un nom de table (avec en option le nom du schéma) ou de
table dérivée, telle qu'une sous-requête, une construction JOIN ou une combinaison complexe de ces
possibilités. Si plus d'une référence de table est listée dans la clause FROM, les tables sont jointes en
croisé (autrement dit, cela réalise un produit cartésien de leurs lignes ; voir ci-dessous). Le résultat de
la liste FROM est une table virtuelle intermédiaire pouvant être sujette aux transformations des clauses
WHERE, GROUP BY et HAVING, et est finalement le résultat des expressions de table.

Lorsqu'une référence de table nomme une table qui est la table parent d'une table suivant la hiérarchie
de l'héritage, la référence de table produit les lignes non seulement de la table, mais aussi des
descendants de cette table, sauf si le mot-clé ONLY précède le nom de la table. Néanmoins, la référence
produit seulement les colonnes qui apparaissent dans la table nommée... Toute colonne ajoutée dans
une sous-table est ignorée.

Au lieu d'écrire ONLY avant le nom de la table, vous pouvez écrire * après le nom de la table pour
indiquer spécifiquement que les tables filles sont incluses. Il n'y a plus de vraie raison pour encore
utiliser cette syntaxe, car chercher dans les tables descendantes est maintenant le comportement par
défaut. C'est toutefois supporté pour compatibilité avec des versions plus anciennes.

7.2.1.1. Tables jointes


Une table jointe est une table dérivée de deux autres tables (réelles ou dérivées) suivant les règles
du type de jointure particulier. Les jointures internes (inner), externes (outer) et croisées (cross) sont
disponibles. La syntaxe générale d'une table jointe est :

T1 type_jointure T2 [ condition_jointure ]

Des jointures de tous types peuvent être chaînées ensemble ou imbriquées : une des deux tables ou les
deux tables peuvent être des tables jointes. Des parenthèses peuvent être utilisées autour des clauses
JOIN pour contrôler l'ordre de jointure. Dans l'absence des parenthèses, les clauses JOIN s'imbriquent
de gauche à droite.

Types de jointures
Jointure croisée (cross join)

T1 CROSS JOIN T2

Pour chaque combinaison possible de lignes provenant de T1 et T2 (c'est-à-dire un produit


cartésien), la table jointe contiendra une ligne disposant de toutes les colonnes de T1 suivies par
toutes les colonnes de T2. Si les tables ont respectivement N et M lignes, la table jointe en aura
N * M.

FROM T1 CROSS JOIN T2 est équivalent à FROM T1 INNER JOIN T2 ON TRUE (voir
ci-dessous). C'est aussi équivalent à : FROM T1, T2.

113
Requêtes

Note
Cette dernière équivalence ne convient pas exactement quand plusieurs tables
apparaissent, car JOIN lie de façon plus profonde que la virgule. Par exemple, FROM T1
CROSS JOIN T2 INNER JOIN T3 ON condition n'est pas identique à FROM
T1, T2 INNER JOIN T3 ON condition, car condition peut faire référence
à T1 dans le premier cas, mais pas dans le second.

Jointures qualifiées (qualified joins)

T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2


ON expression_booleenne
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING
( liste des colonnes jointes )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

Les mots INNER et OUTER sont optionnels dans toutes les formes. INNER est la valeur par
défaut ; LEFT, RIGHT et FULL impliquent une jointure externe.

La condition de la jointure est spécifiée dans la clause ON ou USING, ou implicitement par le


mot NATURAL. La condition de jointure détermine les lignes des deux tables sources considérées
comme « correspondante », comme l'explique le paragraphe ci-dessous.

Les types possibles de jointures qualifiées sont :

INNER JOIN

Pour chaque ligne R1 de T1, la table jointe a une ligne pour chaque ligne de T2 satisfaisant
la condition de jointure avec R1.

LEFT OUTER JOIN

Tout d'abord, une jointure interne est réalisée. Puis, pour chaque ligne de T1 qui ne satisfait
pas la condition de jointure avec les lignes de T2, une ligne jointe est ajoutée avec des valeurs
NULL dans les colonnes de T2. Du coup, la table jointe a toujours au moins une ligne pour
chaque ligne de T1, quelles que soient les conditions.

RIGHT OUTER JOIN

Tout d'abord, une jointure interne est réalisée. Puis, pour chaque ligne de T2 qui ne satisfait
pas la condition de jointure avec les lignes de T1, une ligne jointe est ajoutée avec des valeurs
NULL dans les colonnes de T1. C'est l'inverse d'une jointure gauche : la table résultante aura
toujours une ligne pour chaque ligne de T2, quelles que soient les conditions.

FULL OUTER JOIN

Tout d'abord, une jointure interne est réalisée. Puis, pour chaque ligne de T1 qui ne satisfait
pas la condition de jointure avec les lignes de T2, une ligne jointe est ajoutée avec des valeurs
NULL dans les colonnes de T2. De plus, pour chaque ligne de T2 qui ne satisfait pas la
condition de jointure avec les lignes de T1, une ligne jointe est ajoutée avec des valeurs NULL
dans les colonnes de T1.

La clause ON est le type de condition de jointure le plus utilisé : elle prend une valeur booléenne
du même type que celle utilisée dans une clause WHERE. Une paire de lignes provenant de T1 et
de T2 correspondent si l'expression de la clause ON vaut true.

La clause USING est un raccourci qui vous permet de prendre avantage d'une situation spécifique
où les deux côtés de la jointure utilisent le même nom pour la colonne jointe. Elle prend une liste
de noms de colonnes partagées, en séparant les noms par des virgules et forme une condition de

114
Requêtes

jointure qui inclut une comparaison d'égalité entre chaque. Par exemple, joindre T1 et T2 avec
USING (a, b) produit la même condition de jointure que la condition ON T1.a = T2.a
AND T1.b = T2.b.

De plus, la sortie de JOIN USING supprime les colonnes redondantes : il n'est pas nécessaire
d'imprimer les colonnes de correspondance, puisqu'elles doivent avoir des valeurs identiques.
Alors que JOIN ON produit toutes les colonnes de T2, JOIN USING produit une seule colonne
pour chaque paire de colonnes listées (dans l'ordre listé), suivi par chaque colonne restante
provenant de T1, suivi par chaque colonne restante provenant de T2.

Enfin, NATURAL est un raccourci de USING : il forme une liste USING constituée de tous
les noms de colonnes apparaissant dans les deux tables en entrée. Comme avec USING, ces
colonnes apparaissent une fois seulement dans la table en sortie. S'il n'existe aucun nom commun
de colonne, NATURAL JOIN se comporte comme JOIN ... ON TRUE et produit une jointure
croisée.

Note
USING est raisonnablement protégé contre les changements de colonnes dans les relations
jointes, car seuls les noms de colonnes listés sont combinés. NATURAL est considéré
comme plus risqué, car toute modification de schéma causant l'apparition d'un nouveau
nom de colonne correspondant fera en sorte de joindre la nouvelle colonne.

Pour rassembler tout ceci, supposons que nous avons une table t1 :

no | nom
----+------
1 | a
2 | b
3 | c

et une table t2 :

no | valeur
----+-------
1 | xxx
3 | yyy
5 | zzz

Nous obtenons les résultats suivants pour les différentes jointures :

=> SELECT * FROM t1 CROSS JOIN t2;


no | nom | no | valeur
----+-----+----+-------
1 | a | 1 | xxx
1 | a | 3 | yyy
1 | a | 5 | zzz
2 | b | 1 | xxx
2 | b | 3 | yyy
2 | b | 5 | zzz
3 | c | 1 | xxx
3 | c | 3 | yyy
3 | c | 5 | zzz
(9 rows)

=> SELECT * FROM t1 INNER JOIN t2 ON t1.no = t2.no;


no | nom | no | valeur

115
Requêtes

----+-----+----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
(2 rows)

=> SELECT * FROM t1 INNER JOIN t2 USING (no);


no | nom | valeur
----+-----+-------
1 | a | xxx
3 | c | yyy
(2 rows)

=> SELECT * FROM t1 NATURAL INNER JOIN t2;


no | nom | valeur
----+-----+-------
1 | a | xxx
3 | c | yyy
(2 rows)

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.no = t2.no;


no | nom | no | valeur
----+-----+----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
(3 rows)

=> SELECT * FROM t1 LEFT JOIN t2 USING (no);


no | nom | valeur
----+-----+-------
1 | a | xxx
2 | b |
3 | c | yyy
(3 rows)

=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.no = t2.no;


no | nom | no | valeur
----+-----+----+-------
1 | a | 1 | xxx
3 | c | 3 | yyy
| | 5 | zzz
(3 rows)

=> SELECT * FROM t1 FULL JOIN t2 ON t1.no = t2.no;


no | nom | no | valeur
----+-----+----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | 3 | yyy
| | 5 | zzz
(4 rows)

La condition de jointure spécifiée avec ON peut aussi contenir des conditions sans relation directe
avec la jointure. Ceci est utile pour quelques requêtes, mais son utilisation doit avoir été réfléchie.
Par exemple :

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.no = t2.no AND t2.valeur =


'xxx';

116
Requêtes

no | nom | no | valeur
----+-----+----+-------
1 | a | 1 | xxx
2 | b | |
3 | c | |
(3 rows)

Notez que placer la restriction dans la clause WHERE donne un résultat différent :

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value


= 'xxx';
num | name | num | value
-----+------+-----+-------
1 | a | 1 | xxx
(1 row)

Ceci est dû au fait qu'une restriction placée dans la clause ON est traitée avant la jointure, alors qu'une
restriction placée dans la clause WHERE est traitée après la jointure. Ceci n'a pas d'importance avec
les jointures internes, mais en a une grande avec les jointures externes.

7.2.1.2. Alias de table et de colonne


Un nom temporaire peut être donné aux tables et aux références de tables complexes, nom qui sera
ensuite utilisé pour référencer la table dérivée dans la suite de la requête. Cela s'appelle un alias de
table.

Pour créer un alias de table, écrivez

FROM reference_table AS alias

ou

FROM reference_table alias

Le mot-clé AS n'est pas obligatoire. alias peut être tout identifiant.

Une application typique des alias de table est l'affectation d'identifieurs courts pour les noms de tables
longs, ce qui permet de garder des clauses de jointures lisibles. Par exemple :

SELECT * FROM nom_de_table_tres_tres_long s


JOIN un_autre_nom_tres_long a ON s.id = a.no;

L'alias devient le nouveau nom de la table en ce qui concerne la requête en cours -- il n'est pas autorisé
de faire référence à la table par son nom original où que ce soit dans la requête. Du coup, ceci n'est
pas valide :

SELECT * FROM mon_table AS m WHERE mon_table.a > 5; -- mauvais

Les alias de table sont disponibles principalement pour aider à l'écriture de requête, mais ils deviennent
nécessaires pour joindre une table avec elle-même, par exemple :

SELECT * FROM personnes AS mere JOIN personnes AS enfant ON mere.id


= enfant.mere_id;

De plus, un alias est requis si la référence de la table est une sous-requête (voir la Section 7.2.1.3).

117
Requêtes

Les parenthèses sont utilisées pour résoudre les ambiguïtés. Dans l'exemple suivant, la première
instruction affecte l'alias b à la deuxième instance de ma_table, mais la deuxième instruction affecte
l'alias au résultat de la jonction :

SELECT * FROM ma_table AS a CROSS JOIN ma_table AS b ...


SELECT * FROM (ma_table AS a CROSS JOIN ma_table) AS b ...

Une autre forme d'alias de tables donne des noms temporaires aux colonnes de la table ainsi qu'à la
table :

FROM reference_table [AS] alias ( colonne1 [, colonne2 [, ...]] )

Si le nombre d'alias de colonnes spécifié est plus petit que le nombre de colonnes dont dispose la table
réelle, les colonnes suivantes ne sont pas renommées. Cette syntaxe est particulièrement utile dans le
cas de jointures avec la même table ou dans le cas de sous-requêtes.

Quand un alias est appliqué à la sortie d'une clause JOIN, l'alias cache le nom original référencé à
l'intérieur du JOIN. Par exemple :

SELECT a.* FROM ma_table AS a JOIN ta_table AS b ON ...

est du SQL valide, mais :

SELECT a.* FROM (ma_table AS a JOIN ta_table AS b ON ...) AS c

n'est pas valide ; l'alias de table a n'est pas visible en dehors de l'alias c.

7.2.1.3. Sous-requêtes
Une sous-requête spécifiant une table dérivée doit être enfermée dans des parenthèses et doit se voir
affecter un alias de table (comme dans Section 7.2.1.2). Par exemple :

FROM (SELECT * FROM table1) AS nom_alias

Cet exemple est équivalent à FROM table1 AS nom_alias. Des cas plus intéressants, qui
ne peuvent pas être réduits à une jointure pleine, surviennent quand la sous-requête implique un
groupement ou un agrégat.

Une sous-requête peut aussi être une liste VALUES :

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))


AS noms(prenom, nom)

De nouveau, un alias de table est requis. Affecter des noms d'alias aux colonnes de la liste VALUES
est optionnel, mais c'est une bonne pratique. Pour plus d'informations, voir Section 7.7.

7.2.1.4. Fonctions de table


Les fonctions de table sont des fonctions produisant un ensemble de lignes composées de types de
données de base (types scalaires) ou de types de données composites (lignes de table). Elles sont
utilisées comme une table, une vue ou une sous-requête de la clause FROM d'une requête. Les colonnes
renvoyées par les fonctions de table peuvent être incluses dans une clause SELECT, JOIN ou WHERE
de la même manière que les colonnes d'une table, vue ou sous-requête.

Les fonctions de table peuvent aussi être combinées en utilisant la syntaxe ROWS FROM, avec les
résultats renvoyés dans des colonnes parallèles ; le nombre de lignes résultantes dans ce cas est celui du
résultat de fonction le plus large. Les résultats ayant moins de colonnes sont alignés avec des valeurs
NULL.

118
Requêtes

appel_fonction [WITH ORDINALITY] [[AS] alias_table [(alias_colonne


[, ... ])]]
ROWS FROM( appel_fonction [, ... ] ) [WITH ORDINALITY]
[[AS] alias_table [(alias_colonne [, ... ])]]

Si la clause WITH ORDINALITY est ajoutée, une colonne supplémentaire de type bigint sera
ajoutée aux colonnes de résultat de la fonction. Cette colonne numérote les lignes de l'ensemble de
résultats de la fonction, en commençant à 1. (Ceci est une généralisation de la syntaxe du standard
SQL pour UNNEST ... WITH ORDINALITY.) Par défaut, la colonne ordinale est appelée
ordinality, mais un nom de colonne différent peut être affecté en utilisant une clause AS.

La fonction de table UNNEST peut être appelée avec tout nombre de paramètres tableaux, et envoie
un nombre correspondant de colonnes comme si la fonction UNNEST avait été appelée sur chaque
paramètre séparément (Section 9.18) et combinée en utilisant la construction ROWS FROM.

UNNEST( expression_tableau [, ... ] ) [WITH ORDINALITY]


[[AS] alias_table [(alias_colonne [, ... ])]]

Si aucun alias_table n'est précisé, le nom de la fonction est utilisé comme nom de table ; dans le
cas d'une construction ROWS FROM(), le nom de la première fonction est utilisé.

Si des alias de colonnes ne sont pas fournis pour une fonction renvoyant un type de données de base,
alors le nom de la colonne est aussi le même que le nom de la fonction. Pour une fonction renvoyant
un type composite, les colonnes résultats obtiennent les noms des attributs individuels du type.

Quelques exemples :

CREATE TABLE truc (trucid int, trucsousid int, trucnom text);

CREATE FUNCTION recuptruc(int) RETURNS SETOF truc AS $$


SELECT * FROM truc WHERE trucid = $1;
$$ LANGUAGE SQL;

SELECT * FROM recuptruc(1) AS t1;

SELECT * FROM truc


WHERE trucsousid IN (
SELECT trucsousid
FROM recuptruc(truc.trucid) z
WHERE z.trucid = truc.trucid);

CREATE VIEW vue_recuptruc AS SELECT * FROM recuptruc(1);


SELECT * FROM vue_recuptruc;

Dans certains cas, il est utile de définir des fonctions de table pouvant renvoyer des ensembles de
colonnes différentes suivant la façon dont elles sont appelées. Pour supporter ceci, la fonction de table
est déclarée comme renvoyant le pseudotype record sans paramètres OUT. Quand une telle fonction
est utilisée dans une requête, la structure de ligne attendue doit être spécifiée dans la requête elle-
même, de façon à ce que le système sache comment analyser et planifier la requête. Cette syntaxe
ressemble à ceci :

appel_fonction [AS] alias (définition_colonne [, ... ])


appel_fonction AS [alias] (définition_colonne [, ... ])
ROWS FROM( ... appel_fonction AS (définition_colonne [, ... ])
[, ... ] )

119
Requêtes

Lorsque la syntaxe ROWS FROM() n'est pas utilisée, la liste définition_colonne remplace
la liste d'alias de colonnes qui aurait été autrement attachée à la clause FROM ; les noms dans
les définitions de colonnes servent comme alias de colonnes. Lors de l'utilisation de la syntaxe
ROWS FROM(), une liste définition_colonne peut être attachée à chaque fonction membre
séparément ; ou s'il existe seulement une fonction membre et pas de clause WITH ORDINALITY,
une liste column_definition peut être écrite au lieu de la liste d'alias de colonnes suivant ROWS
FROM().

Considérez cet exemple :

SELECT *
FROM dblink('dbname=mabd', 'SELECT proname, prosrc FROM
pg_proc')
AS t1(proname nom, prosrc text)
WHERE proname LIKE 'bytea%';

La fonction dblink (qui fait partie du module dblink) exécute une requête distante. Elle déclare
renvoyer le type record, car elle pourrait être utilisée pour tout type de requête. L'ensemble de
colonnes réelles doit être spécifié dans la requête appelante de façon à ce que l'analyseur sache, par
exemple, comment étendre *.

Cet exemple utilise ROWS FROM :

SELECT *
FROM ROWS FROM
(
json_to_recordset('[{"a":40,"b":"foo"},
{"a":"100","b":"bar"}]')
AS (a INTEGER, b TEXT),
generate_series(1, 3)
) AS x (p, q, s)
ORDER BY p;

p | q | s
-----+-----+---
40 | foo | 1
100 | bar | 2
| | 3

Il joint deux fonctions en une seule cible FROM. json_to_recordset() doit renvoyer
deux colonnes, la première de type integer et la seconde de type text. Le résultat de
generate_series() est utilisé directement. La clause ORDER BY trie les valeurs de la colonne
en tant qu'entiers.

7.2.1.5. Sous-requêtes LATERAL


Les sous-requêtes apparaissant dans la clause FROM peuvent être précédées du mot-clé LATERAL.
Ceci leur permet de référencer les colonnes fournies par les éléments précédents dans le FROM. (Sans
LATERAL, chaque sous-requête est évaluée indépendamment et ne peut donc pas référencer les autres
éléments de la clause FROM.)

Les fonctions renvoyant des ensembles et apparaissant dans le FROM peuvent aussi être précédées du
mot-clé LATERAL, mais, pour les fonctions, le mot-clé est optionnel. Les arguments de la fonction
peuvent contenir des références aux colonnes fournies par les éléments précédents dans le FROM.

Un élément LATERAL peut apparaître au niveau haut dans la liste FROM ou dans un arbre de jointures
(JOIN). Dans ce dernier cas, cela peut aussi faire référence à tout élément qui est sur le côté gauche
d'un JOIN, alors qu'il est positionné sur sa droite.

120
Requêtes

Quand un élément FROM contient des références croisées LATERAL, l'évaluation se fait ainsi : pour
chaque ligne d'un élément FROM fournissant les colonnes référencées, ou pour chaque ensemble
de lignes de plusieurs éléments FROM fournissant les colonnes, l'élément LATERAL est évalué en
utilisant cette valeur de ligne ou cette valeur d'ensemble de lignes. Les lignes résultantes sont jointes
comme d'habitude aux lignes résultant du calcul. C'est répété pour chaque ligne ou ensemble de lignes
provenant de la table source.

Un exemple trivial de LATERAL est

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id =


foo.bar_id) ss;

Ceci n'est pas vraiment utile, car cela revient exactement au même résultat que cette écriture plus
conventionnelle :

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

LATERAL est principalement utile lorsqu'une colonne référencée est nécessaire pour calculer la
colonne à joindre. Une utilisation habituelle est de fournir une valeur d'un argument à une fonction
renvoyant un ensemble de lignes. Par exemple, supposons que vertices(polygon) renvoie
l'ensemble de sommets d'un polygone, nous pouvons identifier les sommets proches des polygones
stockés dans une table avec la requête suivante :

SELECT p1.id, p2.id, v1, v2


FROM polygones p1, polygones p2,
LATERAL vertices(p1.poly) v1,
LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

Cette requête pourrait aussi être écrite ainsi :

SELECT p1.id, p2.id, v1, v2


FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

ou dans diverses autres formulations équivalentes. (Nous l'avons déjà mentionné, le mot-clé LATERAL
est inutile dans cet exemple, mais nous l'utilisons pour plus de clarté.)

Il est souvent particulièrement utile d'utiliser LEFT JOIN sur une sous-requête LATERAL, pour que
les lignes sources apparaissent dans le résultat même si la sous-requête LATERAL ne produit aucune
ligne pour elles. Par exemple, si get_product_names() renvoie les noms des produits réalisés
par un manufacturier, mais que quelques manufacturiers dans notre table ne réalisent aucun produit,
nous pourrions les trouver avec cette requête :

SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id)
pname ON true
WHERE pname IS NULL;

121
Requêtes

7.2.2. Clause WHERE


La syntaxe de la la section intitulée « Clause WHERE » est

WHERE condition_recherche

où condition_recherche est toute expression de valeur (voir la Section 4.2) renvoyant une
valeur de type boolean.

Après le traitement de la clause FROM, chaque ligne de la table virtuelle dérivée est vérifiée avec la
condition de recherche. Si le résultat de la vérification est positif (true), la ligne est conservée dans la
table de sortie, sinon (c'est-à-dire si le résultat est faux ou nul), la ligne est abandonnée. La condition
de recherche référence typiquement au moins une colonne de la table générée dans la clause FROM ;
ceci n'est pas requis, mais, dans le cas contraire, la clause WHERE n'aurait aucune utilité.

Note
La condition de jointure d'une jointure interne peut être écrite soit dans la clause WHERE soit
dans la clause JOIN. Par exemple, ces expressions de tables sont équivalentes :

FROM a, b WHERE a.id = b.id AND b.val > 5

et :

FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5

ou même peut-être :

FROM a NATURAL JOIN b WHERE b.val > 5

Laquelle utiliser est plutôt une affaire de style. La syntaxe JOIN dans la clause FROM n'est
probablement pas aussi portable vers les autres systèmes de gestion de bases de données SQL,
même si cela fait partie du standard SQL. Pour les jointures externes, il n'y a pas d'autres
choix : elles doivent être faites dans la clause FROM. La clause ON ou USING d'une jointure
externe n'est pas équivalente à une condition WHERE parce qu'elle détermine l'ajout de lignes
(pour les lignes qui ne correspondent pas en entrée) ainsi que pour la suppression de lignes
dans le résultat final.

Voici quelques exemples de clauses WHERE :

SELECT ... FROM fdt WHERE c1 > 5

SELECT ... FROM fdt WHERE c1 IN (1, 2, 3)

SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2)

SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 =


fdt.c1 + 10)

SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 =


fdt.c1 + 10) AND 100

SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 >
fdt.c1)

fdt est la table dérivée dans la clause FROM. Les lignes qui ne correspondent pas à la condition de
recherche de la clause WHERE sont éliminées de la table fdt. Notez l'utilisation de sous-requêtes

122
Requêtes

scalaires en tant qu'expressions de valeurs. Comme n'importe quelle autre requête, les sous-requêtes
peuvent employer des expressions de tables complexes. Notez aussi comment fdt est référencée dans
les sous-requêtes. Qualifier c1 comme fdt.c1 est seulement nécessaire si c1 est aussi le nom d'une
colonne dans la table d'entrée dérivée de la sous-requête. Mais qualifier le nom de colonne ajoute de la
clarté même lorsque cela n'est pas nécessaire. Cet exemple montre comment le nom de colonne d'une
requête externe est étendu dans les requêtes internes.

7.2.3. Clauses GROUP BY et HAVING


Après avoir passé le filtre WHERE, la table d'entrée dérivée peut être sujette à un regroupement en
utilisant la clause GROUP BY et à une élimination de groupe de lignes avec la clause HAVING.

SELECT liste_selection
FROM ...
[WHERE ...]
GROUP
BY reference_colonne_regroupement[,reference_colonne_regroupement]...

La la section intitulée « Clause GROUP BY » est utilisée pour regrouper les lignes d'une table qui ont
les mêmes valeurs dans toutes les colonnes précisées. L'ordre dans lequel ces colonnes sont indiquées
importe peu. L'effet est de combiner chaque ensemble de lignes partageant des valeurs communes
en un seul groupe de lignes représentant toutes les lignes du groupe. Ceci est fait pour éliminer les
redondances dans la sortie et/ou pour calculer les agrégats s'appliquant à ces groupes. Par exemple :

=> SELECT * FROM test1;


x | y
---+---
a | 3
c | 2
b | 5
a | 1
(4 rows)

=> SELECT x FROM test1 GROUP BY x;


x
---
a
b
c
(3 rows)

Dans la seconde requête, nous n'aurions pas pu écrire SELECT * FROM test1 GROUP BY x
parce qu'il n'existe pas une seule valeur pour la colonne y pouvant être associée avec chaque autre
groupe. Les colonnes de regroupement peuvent être référencées dans la liste de sélection, car elles ont
une valeur constante unique par groupe.

En général, si une table est groupée, les colonnes qui ne sont pas listées dans le GROUP BY ne peuvent
pas être référencées sauf dans les expressions d'agrégats. Voici un exemple d'expression d'agrégat :

=> SELECT x, sum(y) FROM test1 GROUP BY x;


x | sum
---+-----
a | 4
b | 5
c | 2
(3 rows)

Ici, sum est la fonction d'agrégat qui calcule une seule valeur pour le groupe entier. La Section 9.20
propose plus d'informations sur les fonctions d'agrégats disponibles.

123
Requêtes

Astuce
Le regroupement sans expressions d'agrégats calcule effectivement l'ensemble des valeurs
distinctes d'une colonne. Ceci peut aussi se faire en utilisant la clause DISTINCT (voir la
Section 7.3.3).

Voici un autre exemple : il calcule les ventes totales pour chaque produit (plutôt que le total des ventes
sur tous les produits) :

SELECT id_produit, p.nom, (sum(v.unite) * p.prix) AS ventes


FROM produits p LEFT JOIN ventes v USING (id_produit)
GROUP BY id_produit, p.nom, p.prix;

Dans cet exemple, les colonnes id_produit, p.nom et p.prix doivent être dans la clause GROUP
BY, car elles sont référencées dans la liste de sélection de la requête (mais voir plus loin). La colonne
v.unite n'a pas besoin d'être dans la liste GROUP BY, car elle est seulement utilisée dans l'expression
de l'agrégat (sum(...)) représentant les ventes d'un produit. Pour chaque produit, la requête renvoie
une ligne de résumé sur les ventes de ce produit.

Si la table produits est configurée de façon à ce que id_produit soit la clé primaire, alors il serait
suffisant de grouper par la colonne id_produit dans l'exemple ci-dessus, car le nom et le prix
seraient dépendants fonctionnellement de l'identifiant du produit, et donc il n'y aurait pas d'ambiguïté
sur le nom et le prix à renvoyer pour chaque groupe d'identifiants de produits.

En SQL strict, GROUP BY peut seulement grouper les colonnes de la table source, mais PostgreSQL
étend ceci en autorisant GROUP BY à grouper aussi les colonnes de la liste de sélection. Grouper par
expressions de valeurs au lieu de simples noms de colonnes est aussi permis.

Si une table a été groupée en utilisant la clause GROUP BY, mais que seuls certains groupes sont
intéressants, la clause HAVING peut être utilisée, comme une clause WHERE, pour éliminer les groupes
du résultat. Voici la syntaxe :

SELECT liste_selection FROM ... [WHERE ...] GROUP BY ...


HAVING expression_booléenne

Les expressions de la clause HAVING peuvent référer à la fois aux expressions groupées et aux
expressions non groupées (ce qui implique nécessairement une fonction d'agrégat).

Exemple :

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;


x | sum
---+-----
a | 4
b | 5
(2 rows)

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';


x | sum
---+-----
a | 4
b | 5
(2 rows)

De nouveau, un exemple plus réaliste :

SELECT id_produit, p.nom, (sum(v.unite) * (p.prix - p.cout)) AS


profit

124
Requêtes

FROM produits p LEFT JOIN ventes v USING (id_produit)


WHERE v.date > CURRENT_DATE - INTERVAL '4 weeks'
GROUP BY id_produit, p.nom, p.prix, p.cout
HAVING sum(p.prix * s.unite) > 5000;

Dans l'exemple ci-dessus, la clause WHERE sélectionne les lignes par une colonne qui n'est pas groupée
(l'expression est vraie seulement pour les ventes des quatre dernières semaines) alors que la clause
HAVING restreint la sortie aux groupes dont le total des ventes dépasse 5000. Notez que les expressions
d'agrégats n'ont pas besoin d'être identiques dans toutes les parties d'une requête.

Si une requête contient des appels à des fonctions d'agrégat, mais pas de clause GROUP BY, le
regroupement a toujours lieu : le résultat est une seule ligne de regroupement (ou peut-être pas de
ligne du tout si la ligne unique est ensuite éliminée par la clause HAVING). Ceci est vrai aussi si elle
comporte une clause HAVING, même sans fonction d'agrégat ou GROUP BY.

7.2.4. GROUPING SETS, CUBE et ROLLUP


Des opérations de regroupements plus complexes que celles décrites ci-dessus sont possibles en
utilisant la notion d'ensembles de regroupement. Les données sélectionnées par les clauses FROM et
WHERE sont regroupées séparément pour chaque ensemble de regroupement indiqué, les agrégats
calculés pour chaque ensemble de la même manière que pour la clause simple GROUP BY, puis le
résultat est retourné. Par exemple:

=> SELECT * FROM ventes;


produit | taille | vendus
-------+------+-------
Foo | L | 10
Foo | M | 20
Bar | M | 15
Bar | L | 5
(4 rows)

=> SELECT produit, taille, sum(vendus) FROM ventes GROUP BY


GROUPING SETS ((produit), (taille), ());
produit | taille | sum
-------+------+-----
Foo | | 30
Bar | | 20
| L | 15
| M | 35
| | 50
(5 rows)

Chaque sous-liste de GROUPING SETS peut indiquer 0 ou plusieurs colonnes ou expressions et est
interprétée de la même manière que si elle était directement dans la clause GROUP BY. Un ensemble
de regroupement vide signifie que toutes les lignes sont agrégées pour former un simple groupe (qui
est renvoyé quand bien même aucune ligne ne serait sélectionnée), comme décrit ci-dessus dans le cas
de fonctions d'agrégat sans clause GROUP BY.

Les références aux colonnes de regroupement ou expressions sont remplacées par des valeurs NULL
dans les lignes renvoyées pour les ensembles de regroupement où ces colonnes n'apparaissent pas.
Pour identifier à quel ensemble de regroupement une ligne en particulier appartient, référez-vous à
Tableau 9.56.

Une notation raccourcie est fournie pour indiquer deux types classiques d'ensembles de regroupement.
Une clause sous la forme

125
Requêtes

ROLLUP ( e1, e2, e3, ... )

représente la liste indiquée d'expressions ainsi que l'ensemble des préfixes de la liste, y compris la
liste vide. C'est donc équivalent à

GROUPING SETS (
( e1, e2, e3, ... ),
...
( e1, e2 ),
( e1 ),
( )
)

Cette notation est communément utilisée avec des données hiérarchiques ; par exemple, le total des
salaires par département, division et sur l'ensemble de l'entreprise.

Une clause sous la forme

CUBE ( e1, e2, ... )

représente la liste indiquée ainsi que l'ensemble des sous-ensembles possibles. De ce fait,

CUBE ( a, b, c )

est équivalent à

GROUPING SETS (
( a, b, c ),
( a, b ),
( a, c ),
( a ),
( b, c ),
( b ),
( c ),
( )
)

Les éléments individuels des clauses CUBE ou ROLLUP peuvent être des expressions individuelles, ou
des sous-listes d'éléments entre parenthèses. Dans ce dernier cas, les sous-listes sont traitées comme
simple élément pour la génération des ensembles de regroupements individuels. Par exemple :

CUBE ( (a, b), (c, d) )

est équivalent à

GROUPING SETS (
( a, b, c, d ),
( a, b ),

126
Requêtes

( c, d ),
( )
)

et

ROLLUP ( a, (b, c), d )

est équivalent à

GROUPING SETS (
( a, b, c, d ),
( a, b, c ),
( a ),
( )
)

Les éléments CUBE et ROLLUP peuvent être utilisés directement dans la clause GROUP BY, ou
imbriqués à l'intérieur d'une clause GROUPING SETS. Si une clause GROUPING SETS est imbriquée
dans une autre, l'effet est le même que si tous les éléments de la clause la plus imbriquée avaient été
écrits directement dans la clause de niveau supérieur.

Si de multiples clauses de regroupement sont indiquées dans une simple clause GROUP BY, alors
la liste finale des ensembles de regroupements est le produit cartésien des éléments individuels. Par
exemple :

GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))

est équivalent à

GROUP BY GROUPING SETS (


(a, b, c, d), (a, b, c, e),
(a, b, d), (a, b, e),
(a, c, d), (a, c, e),
(a, d), (a, e)
)

Note
La syntaxe (a, b) est normalement reconnue dans les expressions comme un constructeur
de ligne. À l'intérieur d'une clause GROUP BY, cette règle ne s'applique pas au premier niveau
d'expressions, et (a, b) est reconnu comme une liste d'expressions, comme décrit ci-dessus.
Si pour une quelconque raison vous avez besoin d'un constructeur de ligne dans une expression
de regroupement, utilisez ROW(a, b).

7.2.5. Traitement de fonctions Window


Si la requête contient une des fonctions Window (voir Section 3.5, Section 9.21 et Section 4.2.8), ces
fonctions sont évaluées après que sont effectués les regroupements, les agrégations, les filtrages par

127
Requêtes

HAVING. C'est-à-dire que si la requête comporte des agrégats, GROUP BY ou HAVING, alors les
enregistrements vus par les fonctions Window sont les lignes regroupées à la place des enregistrements
originaux provenant de FROM/WHERE.

Quand des fonctions Window multiples sont utilisées, toutes les fonctions Window ayant des clauses
PARTITION BY et ORDER BY syntaxiquement équivalentes seront à coup sûr évaluées en une seule
passe sur les données. Par conséquent, elles verront le même ordre de tri, même si ORDER BY ne
détermine pas de façon unique un tri. Toutefois, aucune garantie n'est faite à propos de l'évaluation
de fonctions ayant des spécifications de PARTITION BY ou ORDER BY différentes. (Dans ces cas,
une étape de tri est généralement nécessaire entre les passes d'évaluations de fonctions Window, et le
tri ne garantit pas la préservation de l'ordre des enregistrements que son ORDER BY estime comme
identiques.)

À l'heure actuelle, les fonctions Window nécessitent toujours des données prétriées, ce qui fait que la
sortie de la requête sera triée suivant l'une ou l'autre des clauses PARTITION BY/ORDER BY des
fonctions Window. Il n'est toutefois pas recommandé de s'en servir. Utilisez une clause ORDER BY au
plus haut niveau de la requête si vous voulez être sûr que vos résultats soient triés d'une certaine façon.

7.3. Listes de sélection


Comme montré dans la section précédente, l'expression de table pour la commande SELECT construit
une table virtuelle intermédiaire en combinant les tables, vues, en éliminant les lignes, en groupant,
etc. Cette table est finalement passée à la réalisation de la liste de sélection. Cette liste détermine les
colonnes de la table intermédiaire à afficher.

7.3.1. Éléments de la liste de sélection


La forme la plus simple de liste de sélection est *. C'est un raccourci pour indiquer toutes les colonnes
que l'expression de table produit. Sinon, une liste de sélection est une liste d'expressions de valeurs
séparées par des virgules (comme défini dans la Section 4.2). Par exemple, cela pourrait être une liste
des noms de colonnes :

SELECT a, b, c FROM ...

Les noms de colonnes a, b et c sont soit les noms actuels des colonnes des tables référencées dans la
clause FROM, soit les alias qui leur ont été donnés (voir l'explication dans Section 7.2.1.2). L'espace de
nom disponible dans la liste de sélection est le même que dans la clause WHERE sauf si le regroupement
est utilisé, auquel cas c'est le même que dans la clause HAVING.

Si plus d'une table a une colonne du même nom, le nom de la table doit aussi être donné, comme dans :

SELECT tbl1.a, tbl2.a, tbl1.b FROM ...

En travaillant avec plusieurs tables, il est aussi utile de demander toutes les colonnes d'une table
particulière :

SELECT tbl1.*, tbl2.a FROM ...

Voir Section 8.16.5 pour plus d'informations sur la syntaxe nom_table.*.

Si une expression de valeur arbitraire est utilisée dans la liste de sélection, il ajoute conceptuellement
une nouvelle colonne virtuelle dans la table renvoyée. L'expression de valeur est évaluée une fois pour
chaque ligne avec une substitution des valeurs de lignes avec les références de colonnes. Mais les
expressions de la liste de sélection n'ont pas à référencer les colonnes dans l'expression de la table de
la clause FROM ; elles pourraient être des expressions arithmétiques constantes, par exemple.

7.3.2. Labels de colonnes

128
Requêtes

Les entrées de la liste de sélection peuvent se voir affecter des noms pour la suite de l'exécution,
peut-être pour référence dans une clause ORDER BY ou pour affichage par l'application cliente. Par
exemple :

SELECT a AS valeur, b + c AS sum FROM ...

Si aucun nom de colonne en sortie n'est spécifié en utilisant AS, le système affecte un nom de colonne
par défaut. Pour les références de colonne simple, c'est le nom de la colonne référencée. Pour les appels
de fonction, il s'agit du nom de la fonction. Pour les expressions complexes, le système générera un
nom générique.

Le mot-clé AS est optionnel, mais seulement si le nouveau nom de colonne ne correspond à aucun
des mots-clés PostgreSQL (voir Annexe C). Pour éviter une correspondance accidentelle à un mot-
clé, vous pouvez mettre le nom de colonne entre guillemets. Par exemple, VALUE est un mot-clé, ce
qui fait que ceci ne fonctionne pas :

SELECT a value, b + c AS somme FROM ...

mais ceci fonctionne :

SELECT a "value", b + c AS somme FROM ...

Pour vous protéger de possibles ajouts futurs de mots-clés, il est recommandé de toujours écrire AS
ou de mettre le nom de colonne de sortie entre guillemets.

Note
Le nom des colonnes en sortie est différent ici de ce qui est fait dans la clause FROM (voir la
Section 7.2.1.2). Il est possible de renommer deux fois la même colonne, mais le nom affecté
dans la liste de sélection est celui qui sera passé.

7.3.3. DISTINCT
Après le traitement de la liste de sélection, la table résultante pourrait être optionnellement sujette à
l'élimination des lignes dupliquées. Le mot-clé DISTINCT est écrit directement après SELECT pour
spécifier ceci :

SELECT DISTINCT liste_selection ...

(au lieu de DISTINCT, le mot-clé ALL peut être utilisé pour spécifier le comportement par défaut,
la récupération de toutes les lignes).

Évidemment, les deux lignes sont considérées distinctes si elles diffèrent dans au moins une valeur de
colonne. Les valeurs NULL sont considérées égales dans cette comparaison.

Autrement, une expression arbitraire peut déterminer quelles lignes doivent être considérées
distinctes :

SELECT DISTINCT ON (expression [, expression ...]) liste_selection


...

Ici, expression est une expression de valeur arbitraire, évaluée pour toutes les lignes. Les lignes
dont toutes les expressions sont égales sont considérées comme dupliquées et seule la première ligne
de cet ensemble est conservée dans la sortie. Notez que la « première ligne » d'un ensemble est non

129
Requêtes

prévisible sauf si la requête est triée sur assez de colonnes pour garantir un ordre unique des colonnes
arrivant dans le filtre DISTINCT (le traitement de DISTINCT ON parvient après le tri de ORDER
BY).

La clause DISTINCT ON ne fait pas partie du standard SQL et est quelques fois considérée comme
étant un mauvais style à cause de la nature potentiellement indéterminée de ses résultats. Avec
l'utilisation judicieuse de GROUP BY et de sous-requêtes dans FROM, la construction peut être évitée,
mais elle représente souvent l'alternative la plus agréable.

7.4. Combiner des requêtes


Les résultats de deux requêtes peuvent être combinés en utilisant les opérations d'ensemble : union,
intersection et différence. La syntaxe est

requete1 UNION [ALL] requete2


requete1 INTERSECT [ALL] requete2
requete1 EXCEPT [ALL] requete2

où requete1 et requete2 sont les requêtes pouvant utiliser toutes les fonctionnalités discutées ici.

UNION ajoute effectivement le résultat de requete2 au résultat de requete1 (bien qu'il n'y ait pas
de garantie qu'il s'agisse de l'ordre dans lequel les lignes sont réellement renvoyées). De plus, il élimine
les lignes dupliquées du résultat, de la même façon que DISTINCT, sauf si UNION ALL est utilisée.

INTERSECT renvoie toutes les lignes qui sont à la fois dans le résultat de requete1 et dans le
résultat de requete2. Les lignes dupliquées sont éliminées sauf si INTERSECT ALL est utilisé.

EXCEPT renvoie toutes les lignes qui sont dans le résultat de requete1 mais pas dans le résultat
de requete2 (ceci est quelquefois appelé la différence entre deux requêtes). De nouveau, les lignes
dupliquées sont éliminées sauf si EXCEPT ALL est utilisé.

Pour calculer l'union, l'intersection ou la différence de deux requêtes, les deux requêtes doivent être
« compatibles pour une union », ce qui signifie qu'elles doivent renvoyer le même nombre de colonnes
et que les colonnes correspondantes doivent avoir des types de données compatibles, comme décrit
dans la Section 10.5.

Les opérations sur les ensembles peuvent être combinées, par exemple :

requete1 UNION requete2 EXCEPT requete3

qui est équivalent à :

(requete1 UNION requete2) EXCEPT requete3

Comme indiqué ici, vous pouvez utiliser les parenthèses pour contrôler l'ordre d'évaluation. Sans les
parenthèses, UNION et EXCEPT font une association de gauche à droite, mais INTERSECT a une
priorité plus forte que ces deux opérateurs. De ce fait :

requete1 UNION requete2 INTERSECT requete3

signifie

requete1 UNION (requete2 INTERSECT requete3)

Vous pouvez aussi entourer une requête individuelle avec des parenthèses. C'est important si la
requête a besoin d'utiliser une des clauses discutées dans les sections suivantes, telles que LIMIT.
Sans les parenthèses, vous obtiendrez soit une erreur de syntaxe soit une interprétation de cette clausse

130
Requêtes

comme s'appliquant à la sortie de l'opération ensembliste plutôt que sur une de ses entrées. Par
exemple :

SELECT a FROM b UNION SELECT x FROM y LIMIT 10

est acceptée, mais signifie :

(SELECT a FROM b UNION SELECT x FROM y) LIMIT 10

et non pas :

SELECT a FROM b UNION (SELECT x FROM y LIMIT 10)

7.5. Tri des lignes


Après qu'une requête a produit une table en sortie (après que la liste de sélection a été traitée), elle
peut être optionnellement triée. Si le tri n'a pas été choisi, les lignes sont renvoyées dans un ordre non
spécifié. Dans ce cas, l'ordre réel dépendra des types de plan de parcours et de jointure et de l'ordre sur
le disque, mais vous ne devez pas vous y fier. Un tri particulier en sortie peut seulement être garanti
si l'étape de tri est choisie explicitement.

La clause ORDER BY spécifie l'ordre de tri :

SELECT liste_selection
FROM expression_table
ORDER BY expression_tri1 [ASC | DESC] [NULLS { FIRST | LAST }]
[, expression_tri2 [ASC | DESC] [NULLS { FIRST | LAST }] ...]

Les expressions de tri peuvent être toute expression qui serait valide dans la liste de sélection des
requêtes. Voici un exemple :

SELECT a, b FROM table1 ORDER BY a + b, c;

Quand plus d'une expression est indiquée, les valeurs suivantes sont utilisées pour trier les lignes qui
sont identiques aux valeurs précédentes. Chaque expression pourrait être suivie d'un ASC ou DESC
optionnel pour configurer la direction du tri (ascendant ou descendant). L'ordre ASC est la valeur par
défaut. L'ordre ascendant place les plus petites valeurs en premier, où « plus petit » est défini avec
l'opérateur <. De façon similaire, l'ordre descendant est déterminé avec l'opérateur >. 1

Les options NULLS FIRST et NULLS LAST sont utilisées pour déterminer si les valeurs NULL
apparaissent avant ou après les valeurs non NULL après un tri. Par défaut, les valeurs NULL sont
triées comme si elles étaient plus grandes que toute valeur non NULL. Autrement dit, NULLS FIRST
est la valeur par défaut pour l'ordre descendant (DESC) et NULLS LAST est la valeur utilisée sinon.

Notez que les options de tri sont considérées indépendamment pour chaque colonne triée. Par exemple,
ORDER BY x, y DESC signifie en fait ORDER BY x ASC, y DESC, ce qui est différent de
ORDER BY x DESC, y DESC.

Une expression_tri peut aussi être, à la place, le nom ou le numéro d'une colonne en sortie,
par exemple :
1
En fait, PostgreSQL utilise la classe d'opérateur B-tree par défaut pour le type de données de l'expression pour déterminer l'ordre de tri avec
ASC et DESC. De façon conventionnelle, les types de données seront initialisés de façon à ce que les opérateurs < et > correspondent à cet
ordre de tri, mais un concepteur des types de données définis par l'utilisateur pourrait choisir de faire quelque chose de différent.

131
Requêtes

SELECT a + b AS sum, c FROM table1 ORDER BY sum;


SELECT a, max(b) FROM table1 GROUP BY a ORDER BY 1;

les deux triant par la première colonne en sortie. Notez qu'un nom de colonne en sortie doit être unique,
il ne doit pas être utilisé dans une expression -- par exemple, ceci n'est pas correct :

SELECT a + b AS sum, c FROM table1 ORDER BY sum + c; --


mauvais

Cette restriction est là pour réduire l'ambiguïté. Il y en a toujours si un élément ORDER BY est un
simple nom qui pourrait correspondre soit à un nom de colonne en sortie soit à une colonne d'une
expression de table. La colonne en sortie est utilisée dans de tels cas. Cela causera seulement de la
confusion si vous utilisez AS pour renommer une colonne en sortie qui correspondra à un autre nom
de colonne d'une table.

ORDER BY peut être appliqué au résultat d'une combinaison UNION, d'une combinaisonINTERSECT
ou d'une combinaison EXCEPT, mais, dans ce cas, il est seulement permis de trier par les noms ou
numéros de colonnes, pas par les expressions.

7.6. LIMIT et OFFSET


LIMIT et OFFSET vous permettent de retrouver seulement une portion des lignes générées par le
reste de la requête :

SELECT liste_selection
FROM expression_table
[ ORDER BY ...]
[ LIMIT { nombre | ALL } ] [OFFSET nombre]

Si un nombre limite est donné, pas plus que ce nombre de lignes ne sera renvoyé (mais peut-être moins
si la requête récupère moins de lignes). LIMIT ALL revient à ne pas spécifier la clause LIMIT.

OFFSET indique de passer ce nombre de lignes avant de renvoyer les lignes restantes. OFFSET 0
revient à oublier la clause OFFSET, tout comme OFFSET avec un argument NULL.

Si à la fois OFFSET et LIMIT apparaissent, alors les OFFSET lignes sont laissées avant de commencer
le renvoi des LIMIT lignes.

Lors de l'utilisation de LIMIT, il est important d'utiliser une clause ORDER BY contraignant les lignes
résultantes dans un ordre unique. Sinon, vous obtiendrez un sous-ensemble non prévisible de lignes de
la requête. Vous pourriez demander les lignes de 10 à 20, mais dans quel ordre ? L'ordre est inconnu
si vous ne spécifiez pas ORDER BY.

L'optimiseur de requêtes prend LIMIT en compte lors de la génération des plans de requêtes, de façon
à ce que vous obteniez différents plans (avec différents ordres de lignes) suivant ce que vous donnez
à LIMIT et OFFSET. Du coup, utiliser des valeurs LIMIT/OFFSET différentes pour sélectionner
des sous-ensembles différents d'un résultat de requête donnera des résultats inconsistants sauf si vous
forcez un ordre de résultat prévisible avec ORDER BY. Ceci n'est pas un bogue ; c'est une conséquence
inhérente au fait que le SQL ne promette pas de délivrer les résultats d'une requête dans un ordre
particulier sauf si ORDER BY est utilisé pour contraindre l'ordre.

Les lignes passées par une clause OFFSET devront toujours être traitées à l'intérieur du serveur ; du
coup, un OFFSET important peut être inefficace.

7.7. Listes VALUES


132
Requêtes

VALUES fournit une façon de générer une table de « constantes » qui peut être utilisée dans une requête
sans avoir à réellement créer et peupler une table sur disque. La syntaxe est

VALUES ( expression [, ...] ) [, ...]

Chaque liste d'expressions entre parenthèses génère une ligne dans la table. Les listes doivent toutes
avoir le même nombre d'éléments (c'est-à-dire une liste de colonnes dans la table), et les entrées
correspondantes dans chaque liste doivent avoir des types compatibles. Le type réel affecté à chaque
colonne du résultat est déterminé en utilisant les mêmes règles que pour UNION (voir Section 10.5).

Voici un exemple :

VALUES (1, 'un'), (2, 'deux'), (3, 'trois');

renverra une table de deux colonnes et trois lignes. C'est équivalent à :

SELECT 1 AS column1, 'un' AS column2


UNION ALL
SELECT 2, 'deux'
UNION ALL
SELECT 3, 'trois';

Par défaut, PostgreSQL affecte les noms column1, column2, etc. aux colonnes d'une table
VALUES. Les noms des colonnes ne sont pas spécifiés par le standard SQL et les différents SGBD
le font de façon différente. Donc, il est généralement mieux de surcharger les noms par défaut avec
une liste d'alias, comme ceci :

=> SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t
(num,letter);
num | letter
-----+--------
1 | one
2 | two
3 | three
(3 rows)

Syntaxiquement, VALUES suivi par une liste d'expressions est traité de la même façon que

SELECT liste_select FROM expression_table

et peut apparaître partout où un SELECT le peut. Par exemple, vous pouvez l'utiliser comme
élément d'un UNION ou y attacher une spécification de tri (ORDER BY, LIMIT et/
ou OFFSET). VALUES est habituellement utilisée comme source de données dans une commande
INSERT command, mais aussi dans une sous-requête.

Pour plus d'informations, voir VALUES.

7.8. Requêtes WITH (Common Table


Expressions)
WITH fournit un moyen d'écrire des ordres auxiliaires pour les utiliser dans des requêtes plus
importantes. Ces requêtes, qui sont souvent appelées Common Table Expressions ou CTE, peuvent

133
Requêtes

être vues comme des tables temporaires qui n'existent que pour une requête. Chaque ordre auxiliaire
dans une clause WITH peut être un SELECT, INSERT, UPDATE, ou DELETE; et la clause WITH
elle-même est attachée à un ordre primaire qui peut lui aussi être un SELECT, INSERT, UPDATE,
ou DELETE.

7.8.1. SELECT dans WITH


L'intérêt de SELECT dans WITH est de diviser des requêtes complexes en parties plus simples. Un
exemple est:

WITH ventes_regionales AS (
SELECT region, SUM(montant) AS ventes_totales
FROM commandes
GROUP BY region
), meilleures_regions AS (
SELECT region
FROM ventes_regionales
WHERE ventes_totales > (SELECT SUM(ventes_totales)/10 FROM
ventes_regionales)
)
SELECT region,
produit,
SUM(quantite) AS unites_produit,
SUM(montant) AS ventes_produit
FROM commandes
WHERE region IN (SELECT region FROM meilleures_regions)
GROUP BY region, produit;

qui affiche les totaux de ventes par produit seulement dans les régions ayant les meilleures
ventes. La clause WITH définit deux ordres auxiliaires appelés ventes_regionales
et meilleures_regions, où la sortie de ventes_regionales est utilisée dans
meilleures_regions et la sortie de meilleures_regions est utilisée dans la requête
SELECT primaire. Cet exemple aurait pu être écrit sans WITH, mais aurait alors nécessité deux niveaux
de sous-SELECT imbriqués. Les choses sont un peu plus faciles à suivre de cette façon.

Le modificateur optionnel RECURSIVE fait passer WITH du statut de simple aide syntaxique à celui
de quelque chose qu'il serait impossible d'accomplir avec du SQL standard. Grâce à RECURSIVE,
une requête WITH peut utiliser sa propre sortie. Un exemple très simple se trouve dans cette requête,
qui ajoute les nombres de 1 à 100 :

WITH RECURSIVE t(n) AS (


VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;

La forme générale d'une requête WITH est toujours un terme non récursif, puis UNION (ou UNION
ALL), puis un terme récursif. Seul le terme récursif peut contenir une référence à la sortie propre de
la requête. Une requête de ce genre est exécutée comme suit :

Évaluation de requête récursive


1. Évaluer le terme non récursif. Pour UNION (mais pas UNION ALL), supprimer les
enregistrements en double. Inclure le reste dans le résultat de la requête récursive et le mettre
aussi dans une table temporaire de travail (working table.)

2. Tant que la table de travail n'est pas vide, répéter ces étapes :

134
Requêtes

a. Évaluer le terme récursif, en substituant à la référence récursive le contenu courant de la


table de travail. Pour UNION (mais pas UNION ALL), supprimer les doublons, ainsi que les
enregistrements en doublon des enregistrements déjà obtenus. Inclure les enregistrements
restants dans le résultat de la requête récursive, et les mettre aussi dans une table temporaire
intermédiaire (intermediate table).

b. Remplacer le contenu de la table de travail par celui de la table intermédiaire, puis supprimer
la table intermédiaire.

Note
Dans son appellation stricte, ce processus est une itération, pas une récursion, mais
RECURSIVE est la terminologie choisie par le comité de standardisation de SQL. Alors que
RECURSIVE autorise la spécification récursive des requêtes, en interne, ce type de requêtes
est évalué itérativement.

Dans l'exemple précédent, la table de travail a un seul enregistrement à chaque étape, et il prend les
valeurs de 1 à 100 en étapes successives. À la centième étape, il n'y a plus de sortie en raison de la
clause WHERE, ce qui met fin à la requête.

Les requêtes récursives sont utilisées généralement pour traiter des données hiérarchiques ou sous
forme d'arbres. Cette requête est un exemple utile pour trouver toutes les sous-parties directes et
indirectes d'un produit, si seule une table donne toutes les inclusions immédiates :

WITH RECURSIVE parties_incluses(sous_partie, partie, quantite) AS (


SELECT sous_partie, partie, quantite FROM parties WHERE partie
= 'notre_produit'
UNION ALL
SELECT p.sous_partie, p.partie, p.quantite * pr.quantite
FROM parties_incluses pr, parties p
WHERE p.partie = pr.sous_partie
)
SELECT sous_partie, SUM(quantite) as quantite_totale
FROM parties_incluses
GROUP BY sous_partie

Quand on travaille avec des requêtes récursives, il est important d'être sûr que la partie récursive de
la requête finira par ne retourner aucun enregistrement, au risque sinon de voir la requête boucler
indéfiniment. Quelquefois, utiliser UNION à la place de UNION ALL peut résoudre le problème en
supprimant les enregistrements qui doublonnent ceux déjà retournés. Toutefois, souvent, un cycle
ne met pas en jeu des enregistrements de sortie qui sont totalement des doublons : il peut s'avérer
nécessaire de vérifier juste un ou quelques champs, afin de s'assurer que le même point a déjà été
atteint précédemment. La méthode standard pour gérer ces situations est de calculer un tableau de
valeurs déjà visitées. Par exemple, observez la requête suivante, qui parcourt une table graphe en
utilisant un champ lien :

WITH RECURSIVE parcourt_graphe(id, lien, donnee, profondeur) AS (


SELECT g.id, g.lien, g.donnee, 1
FROM graphe g
UNION ALL
SELECT g.id, g.lien, g.donnee, sg.profondeur + 1
FROM graphe g, parcourt_graphe sg
WHERE g.id = sg.lien
)
SELECT * FROM parcourt_graphe;

135
Requêtes

Cette requête va boucler si la liaison lien contient des boucles. Parce que nous avons besoin de la
sortie « profondeur », simplement remplacer UNION ALL par UNION ne résoudra pas le problème.
À la place, nous avons besoin d'identifier si nous avons atteint un enregistrement que nous avons déjà
traité pendant notre parcours des liens. Nous ajoutons deux colonnes chemin et boucle à la requête :

WITH RECURSIVE parcourt_graphe(id, lien, donnee, profondeur,


chemin, boucle) AS (
SELECT g.id, g.lien, g.donnee, 1,
ARRAY[g.id],
false
FROM graphe g
UNION ALL
SELECT g.id, g.lien, g.donnee, sg.profondeur + 1,
chemin || g.id,
g.id = ANY(chemin)
FROM graphe g, parcourt_graphe sg
WHERE g.id = sg.lien AND NOT boucle
)
SELECT * FROM parcourt_graphe;

En plus de prévenir les boucles, cette valeur de tableau est souvent pratique en elle-même pour
représenter le « chemin » pris pour atteindre chaque enregistrement.

De façon plus générale, quand plus d'un champ a besoin d'être vérifié pour identifier une boucle,
utilisez un tableau d'enregistrements. Par exemple, si nous avions besoin de comparer les champs f1
et f2 :

WITH RECURSIVE parcourt_graphe(id, lien, donnee, profondeur,


chemin, boucle) AS (
SELECT g.id, g.lien, g.donnee, 1,
ARRAY[ROW(g.f1, g.f2)],
false
FROM graphe g
UNION ALL
SELECT g.id, g.lien, g.donnee, sg.profondeur + 1,
chemin || ROW(g.f1, g.f2),
ROW(g.f1, g.f2) = ANY(chemin)
FROM graphe g, parcourt_graphe sg
WHERE g.id = sg.lien AND NOT boucle
)
SELECT * FROM parcourt_graphe;

Astuce
Omettez la syntaxe ROW() dans le cas courant où un seul champ a besoin d'être testé pour
déterminer une boucle. Ceci permet, par l'utilisation d'un tableau simple plutôt que d'un tableau
de type composite, de gagner en efficacité.

Astuce
L'algorithme d'évaluation récursive de requête produit sa sortie en ordre de parcours en
largeur (algorithme breadth-first). Vous pouvez afficher les résultats en ordre de parcours

136
Requêtes

en profondeur (depth-first) en faisant sur la requête externe un ORDER BY sur une colonne
« chemin » construite de cette façon.

Si vous n'êtes pas certain qu'une requête puisse boucler, une astuce pratique pour la tester est d'utiliser
LIMIT dans la requête parente. Par exemple, cette requête bouclerait indéfiniment sans un LIMIT :

WITH RECURSIVE t(n) AS (


SELECT 1
UNION ALL
SELECT n+1 FROM t
)
SELECT n FROM t LIMIT 100;

Ceci fonctionne parce que l'implémentation de PostgreSQL n'évalue que le nombre d'enregistrements
de la requête WITH récupérés par la requête parente. L'utilisation de cette astuce en production est
déconseillée parce que d'autres systèmes pourraient fonctionner différemment. Par ailleurs, cela ne
fonctionnera pas si vous demandez à la requête externe de trier les résultats de la requête récursive, ou
si vous les joignez à une autre table, parce dans ces cas, la requête extérieure essaiera habituellement
de récupérer toute la sortie de la requête WITH de toute façon.

Une propriété intéressante des requêtes WITH est qu'elles ne sont évaluées qu'une seule fois par
exécution de la requête parente ou des requêtes WITH sœurs. Par conséquent, les calculs coûteux
qui sont nécessaires à plusieurs endroits peuvent être placés dans une requête WITH pour éviter le
travail redondant. Un autre intérêt peut être d'éviter l'exécution multiple d'une fonction ayant des
effets de bord. Toutefois, le revers de la médaille est que l'optimiseur est moins capable d'extrapoler
les restrictions de la requête parente vers une requête WITH que vers une sous-requête classique.
La requête WITH sera généralement exécutée telle quelle, sans suppression d'enregistrements, que
la requête parente devra supprimer ensuite. (Mais, comme mentionné précédemment, l'évaluation
pourrait s'arrêter rapidement si la (les) référence(s) à la requête ne demande(nt) qu'un nombre limité
d'enregistrements).

Les exemples précédents ne montrent que des cas d'utilisation de WITH avec SELECT, mais on peut
les attacher de la même façon à un INSERT, UPDATE, ou DELETE. Dans chaque cas, le mécanisme
fournit en fait des tables temporaires auxquelles on peut faire référence dans la commande principale.

7.8.2. Ordres de Modification de Données avec WITH


Vous pouvez utiliser des ordres de modification de données (INSERT, UPDATE, ou DELETE) dans
WITH. Cela vous permet d'effectuer plusieurs opérations différentes dans la même requête. Par
exemple:

WITH lignes_deplacees AS (
DELETE FROM produits
WHERE
"date" >= '2010-10-01' AND
"date" < '2010-11-01'
RETURNING *
)
INSERT INTO log_produits
SELECT * FROM lignes_deplacees;

Cette requête déplace les enregistrements de produits vers log_produits. Le DELETE du


WITH supprime les enregistrements spécifiés de produits, en retournant leurs contenus par la clause
RETURNING; puis la requête primaire lit cette sortie et l'insère dans log_produits.

137
Requêtes

Un point important à noter de l'exemple précédent est que la clause WITH est attachée à l'INSERT,
pas au sous-SELECT de l' INSERT. C'est nécessaire parce que les ordres de modification de données
ne sont autorisés que dans les clauses WITH qui sont attachées à l'ordre de plus haut niveau. Toutefois,
les règles de visibilité normales de WITH s'appliquent, il est donc possible de faire référence à la sortie
du WITH dans le sous-SELECT.

Les ordres de modification de données dans WITH ont habituellement des clauses RETURNING (voir
Section 6.4), comme dans l'exemple précédent. C'est la sortie de la clause RETURNING, pas la table
cible de l'ordre de modification de données, qui forme la table temporaire à laquelle on pourra faire
référence dans le reste de la requête. Si un ordre de modification de données dans WITH n'a pas de
clause RETURNING, alors il ne produit pas de table temporaire et ne peut pas être utilisé dans le reste
de la requête. Un ordre de ce type sera toutefois exécuté. En voici un exemple (dénué d'intérêt):

WITH t AS (
DELETE FROM foo
)
DELETE FROM bar;

Cet exemple supprimerait tous les éléments des tables foo et bar. Le nombre d'enregistrements
retourné au client n'inclurait que les enregistrements supprimés de bar.

Les autoréférences récursives dans les ordres de modification de données ne sont pas autorisées. Dans
certains cas, il est possible de contourner cette limitation en faisant référence à la sortie d'un WITH,
par exemple:

WITH RECURSIVE pieces_incluses(sous_piece, piece) AS (


SELECT sous_piece, piece FROM pieces WHERE piece =
'notre_produit'
UNION ALL
SELECT p.sous_piece, p.piece
FROM pieces_incluses pr, pieces p
WHERE p.piece = pr.sous_piece
)
DELETE FROM pieces
WHERE piece IN (SELECT piece FROM pieces_incluses);

Cette requête supprimerait toutes les pièces directes et indirectes d'un produit.

Les ordres de modification de données dans WITH sont exécutés exactement une fois, et toujours
jusqu'à la fin, indépendamment du fait que la requête primaire lise tout (ou même une partie) de leur
sortie. Notez que c'est différent de la règle pour SELECT dans WITH: comme précisé dans la section
précédente, l'exécution d'un SELECT n'est poursuivie que tant que la requête primaire consomme sa
sortie.

Les sous-requêtes du WITH sont toutes exécutées simultanément et simultanément avec la requête
principale. Par conséquent, quand vous utilisez un ordre de modification de données avec WITH, l'ordre
dans lequel les mises à jour sont effectuées n'est pas prévisible. Toutes les requêtes sont exécutées
dans le même instantané (voyez Chapitre 13), elles ne peuvent donc pas voir les effets des autres sur
les tables cibles. Ceci rend sans importance le problème de l'imprévisibilité de l'ordre des mises à jour,
et signifie que RETURNING est la seule façon de communiquer les modifications entre les différentes
sous-requêtes WITH et la requête principale. En voici un exemple:

WITH t AS (
UPDATE produits SET prix = prix * 1.05
RETURNING *

138
Requêtes

)
SELECT * FROM produits;

Le SELECT externe retournerait les prix originaux avant l'action de UPDATE, alors qu'avec :

WITH t AS (
UPDATE produits SET prix = prix * 1.05
RETURNING *
)
SELECT * FROM t;

le SELECT externe retournerait les données mises à jour.

Essayer de mettre à jour le même enregistrement deux fois dans le même ordre n'est pas supporté.
Seule une des deux modifications a lieu, mais il n'est pas aisé (et quelquefois impossible) de déterminer
laquelle. Ceci s'applique aussi pour la suppression d'un enregistrement qui a déjà été mis à jour dans le
même ordre : seule la mise à jour est effectuée. Par conséquent, vous devriez éviter en règle générale
de mettre à jour le même enregistrement deux fois en un seul ordre. En particulier, évitez d'écrire
des sous-requêtes qui modifieraient les mêmes enregistrements que la requête principale ou une autre
sous-requête. Les effets d'un ordre de ce type seraient imprévisibles.

À l'heure actuelle, les tables utilisées comme cibles d'un ordre modifiant les données dans un WITH
ne doivent avoir ni règle conditionnelle, ni règle ALSO, ni une règle INSTEAD qui génère plusieurs
ordres.

139
Chapitre 8. Types de données
PostgreSQL offre un large choix de types de données disponibles nativement. Les utilisateurs peuvent
ajouter de nouveaux types à PostgreSQL en utilisant la commande CREATE TYPE.

Le Tableau 8.1 montre tous les types de données généraux disponibles nativement. La plupart des types
de données alternatifs listés dans la colonne « Alias » sont les noms utilisés en interne par PostgreSQL
pour des raisons historiques. Il existe également d'autres types de données internes ou obsolètes, mais
ils ne sont pas listés ici.

Tableau 8.1. Types de données

Nom Alias Description


bigint int8 Entier signé sur huit octets
bigserial serial8 Entier sur huit octets à incrémentation
automatique
bit [ (n) ] Suite de bits de longueur fixe
bit varying [ (n) ] varbit Suite de bits de longueur variable
[ (n) ]
boolean bool Booléen (Vrai/Faux)
box Boîte rectangulaire dans le plan
bytea Donnée binaire (« tableau d'octets »)
character [ (n) ] char Chaîne de caractères de longueur fixe
[ (n) ]
character varying varchar Chaîne de caractères de longueur variable
[ (n) ] [ (n) ]
cidr Adresse réseau IPv4 ou IPv6
circle Cercle dans le plan
date Date du calendrier (année, mois, jour)
double precision float8 Nombre à virgule flottante de double précision
(sur huit octets)
inet Adresse d'ordinateur IPv4 ou IPv6
integer int, int4 Entier signé sur quatre octets
interval [ champs ] Intervalle de temps
[ (p) ]
json Données texte JSON
jsonb Données binaires JSON, décomposées
line Droite (infinie) dans le plan
lseg Segment de droite dans le plan
macaddr Adresse MAC (pour Media Access Control)
macaddr8 Adresse MAC (pour Media Access Control)
(format EUI-64)
money Montant monétaire
numeric [ (p, s) ] decimal Nombre exact dont la précision peut être spécifiée
[ (p, s) ]
path Chemin géométrique dans le plan

140
Types de données

Nom Alias Description


pg_lsn Séquence numérique de journal (Log Sequence
Number) de PostgreSQL
point Point géométrique dans le plan
polygon Chemin géométrique fermé dans le plan
real float4 Nombre à virgule flottante de simple précision
(sur quatre octets)
smallint int2 Entier signé sur deux octets
smallserial serial2 Entier sur deux octets à incrémentation
automatique
serial serial4 Entier sur quatre octets à incrémentation
automatique
text Chaîne de caractères de longueur variable
time [ (p) ] [ without Heure du jour (pas du fuseau horaire)
time zone ]
time [ (p) ] with time timetz Heure du jour, avec fuseau horaire
zone
timestamp [ (p) ] Date et heure (pas du fuseau horaire)
[ without time zone ]
timestamp [ (p) with timestamptz Date et heure, avec fuseau horaire
time zone
tsquery requête pour la recherche plein texte
tsvector document pour la recherche plein texte
txid_snapshot image de l'identifiant de transaction au niveau
utilisateur
uuid identifiant unique universel
xml données XML

Compatibilité
Les types suivants sont conformes à la norme SQL: bigint, bit, bit varying,
boolean, char, character varying, character, varchar, date, double
precision, integer, interval, numeric, decimal, real, smallint, time
(avec et sans fuseau horaire), timestamp (avec et sans fuseau horaire), xml.

Chaque type de données a une représentation externe déterminée par ses fonctions d'entrée et de sortie.
De nombreux types de données internes ont un format externe évident. Cependant, certains types sont
spécifiques à PostgreSQL, comme les chemins géométriques, ou acceptent différents formats, comme
les types de données de date et d'heure. Certaines fonctions d'entrée et de sortie ne sont pas inversables :
le résultat de la fonction de sortie peut manquer de précision comparé à l'entrée initiale.

8.1. Types numériques


Les types numériques sont constitués d'entiers de deux, quatre ou huit octets, de nombres à virgule
flottante de quatre ou huit octets et de décimaux dont la précision peut être indiquée. Le Tableau 8.2
précise les types disponibles.

141
Types de données

Tableau 8.2. Types numériques


Nom Taille de Description Étendue
stockage
smallint 2 octets entier de faible étendue de -32768 à +32767
integer 4 octets entier habituel de -2147483648 à
+2147483647
bigint 8 octets grand entier de -9223372036854775808 à
+9223372036854775807
decimal variable précision indiquée par jusqu'à 131072 chiffres avant le
l'utilisateur, valeur exacte point décimal ; jusqu'à 16383
après le point décimal
numeric variable précision indiquée par jusqu'à 131072 chiffres avant le
l'utilisateur, valeur exacte point décimal ; jusqu'à 16383
après le point décimal
real 4 octets précision variable, valeur précision de 6 décimales
inexacte
double 8 octets précision variable, valeur précision de 15 décimales
precision inexacte
smallserial 2 bytes Entier sur 2 octets à 1 to 32767
incrémentation automatique
serial 4 octets entier à incrémentation de 1 à 2147483647
automatique
bigserial 8 octets entier de grande taille à de 1 à 9223372036854775807
incrémentation automatique

La syntaxe des constantes pour les types numériques est décrite dans la Section 4.1.2. Les types
numériques ont un ensemble complet d'opérateurs arithmétiques et de fonctions. On peut se référer au
Chapitre 9 pour plus d'informations. Les sections suivantes décrivent ces types en détail.

8.1.1. Types entiers


Les types smallint, integer et bigint stockent des nombres entiers, c'est-à-dire sans décimale,
de différentes étendues. Toute tentative d'y stocker une valeur en dehors de l'échelle produit une erreur.

Le type integer est le plus courant. Il offre un bon compromis entre capacité, espace utilisé et
performance. Le type smallint n'est utilisé que si l'économie d'espace disque est le premier critère
de choix. Le type bigint est conçu pour n'être utilisé que si l'échelle de valeurs du type integer
n'est pas suffisante.

SQL ne définit que les types de données integer (ou int), smallint et bigint. Les noms de
types int2, int4, et int8 sont des extensions, partagées par d'autres systèmes de bases de données
SQL.

8.1.2. Nombres à précision arbitraire


Le type numeric peut stocker des nombres contenant un très grand nombre de chiffres. Il est
spécialement recommandé pour stocker les montants financiers et autres quantités pour lesquels
l'exactitude est indispensable. Les calculs avec des valeurs numeric renvoient des résultats exacts
quand c'est possible (addition, soustraction, multiplication). Néanmoins, les calculs sur les valeurs
numeric sont très lents comparés aux types entiers ou aux types à virgule flottante décrits dans la
section suivante.

Dans ce qui suit, on utilise les termes suivants. La précision d'un numeric est le nombre total de
chiffres significatifs dans le nombre complet, c'est-à-dire le nombre de chiffres de part et d'autre du

142
Types de données

séparateur. L'échelle d'un numeric est le nombre de chiffres décimaux de la partie fractionnaire, à
droite du séparateur de décimales. Donc, le nombre 23.5141 a une précision de 6 et une échelle de 4.
On peut considérer que les entiers ont une échelle de 0.

La précision maximale et l'échelle maximale d'une colonne numeric peuvent être toutes deux
réglées. Pour déclarer une colonne de type numérique, il faut utiliser la syntaxe :

NUMERIC(précision, échelle)

La précision doit être strictement positive, l'échelle positive ou NULL. Alternativement :

NUMERIC(précision)

indique une échelle de 0.

NUMERIC

sans précision ni échelle crée une colonne dans laquelle on peut stocker des valeurs de n'importe
quelle précision ou échelle, dans la limite de la précision implantée. Une colonne de ce type n'impose
aucune précision à la valeur entrée, alors que les colonnes numeric ayant une échelle forcent les
valeurs entrées à cette échelle. (Le standard SQL demande une précision par défaut de 0, c'est-à-dire
de forcer la transformation en entier. Les auteurs trouvent cela inutile. Dans un souci de portabilité, il
est préférable de toujours indiquer explicitement la précision et l'échelle.)

Note
La précision maximale autorisée, si elle est explicitement spécifiée dans la déclaration du type,
est de 1000. NUMERIC sans précision est sujet aux limites décrites dans Tableau 8.2.

Si l'échelle d'une valeur à stocker est supérieure à celle de la colonne, le système arrondit la valeur au
nombre de décimales indiqué pour la colonne. Si le nombre de chiffres à gauche du point décimal est
supérieur à la différence entre la précision déclarée et l'échelle déclarée, une erreur est levée.

Les valeurs numériques sont stockées physiquement sans zéro avant ou après. Du coup, la précision
déclarée et l'échelle de la colonne sont des valeurs maximales, pas des allocations fixes (en ce sens, le
type numérique est plus proche de varchar(n) que de char(n)). Le besoin pour le stockage réel
est de deux octets pour chaque groupe de quatre chiffres décimaux, plus trois à huit octets d'en-tête.

En plus des valeurs numériques ordinaires, le type numeric autorise la valeur spéciale NaN qui
signifie « not-a-number » (NdT : pas un nombre). Toute opération sur NaN retourne NaN. Pour écrire
cette valeur comme une constante dans une requête SQL, elle doit être placée entre guillemets. Par
exemple, UPDATE table SET x = 'NaN'. En saisie, la chaîne NaN est reconnue, quelle que
soit la casse utilisée.

Note
Dans la plupart des implémentations du concept « not-a-number », NaN est considéré différent
de toute valeur numérique (ceci incluant NaN). Pour autoriser le tri des valeurs de type
numeric et les utiliser dans des index basés sur le tri, PostgreSQL traite les valeurs NaN
comme identiques entre elles, mais toutes supérieures aux valeurs non NaN.

Les types decimal et numeric sont équivalents. Les deux types sont dans le standard SQL.

Lors de l'arrondissement de valeurs, le type numeric arrondit en s'éloignant de zéro, alors que (sur
la plupart des machines) les types real et double precision arrondissent vers le nombre le
plus proche. Par exemple :

143
Types de données

SELECT x,
round(x::numeric) AS num_round,
round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
x | num_round | dbl_round
------+-----------+-----------
-3.5 | -4 | -4
-2.5 | -3 | -2
-1.5 | -2 | -2
-0.5 | -1 | -0
0.5 | 1 | 0
1.5 | 2 | 2
2.5 | 3 | 2
3.5 | 4 | 4
(8 rows)

8.1.3. Types à virgule flottante


Les types de données real et double precision sont des types numériques inexacts de précision
variable. En pratique, ils sont généralement conformes à la norme IEEE 754 pour l'arithmétique binaire
à virgule flottante (respectivement simple et double précision), dans la mesure où les processeurs, le
système d'exploitation et le compilateur les supportent.

Inexact signifie que certaines valeurs ne peuvent être converties exactement dans le format interne.
Elles sont, de ce fait, stockées sous une forme approchée. Ainsi, stocker puis réafficher ces valeurs
peut faire apparaître de légers écarts. Prendre en compte ces erreurs et la façon dont elles se propagent
au cours des calculs est le sujet d'une branche entière des mathématiques et de l'informatique, qui n'est
pas le sujet de ce document, à l'exception des points suivants :

• pour un stockage et des calculs exacts, comme pour les valeurs monétaires, le type numeric doit
être privilégié ;

• pour des calculs compliqués avec ces types pour quoi que ce soit d'important, et particulièrement
pour le comportement aux limites (infini, zéro), l'implantation spécifique à la plate-forme doit être
étudiée avec soin ;

• tester l'égalité de deux valeurs à virgule flottante peut ne pas donner le résultat attendu.

Sur la plupart des plates-formes, le type real a une étendue d'au moins 1E-37 à 1E37 avec une
précision d'au moins six chiffres décimaux. Le type double precision a généralement une
étendue de 1E-307 à 1E+308 avec une précision d'au moins quinze chiffres. Les valeurs trop grandes
ou trop petites produisent une erreur. Un arrondi peut avoir lieu si la précision d'un nombre en entrée
est trop grande. Les nombres trop proches de zéro qui ne peuvent être représentés autrement que par
zéro produisent une erreur (underflow).

Note
Le paramètre extra_float_digits contrôle le nombre de chiffres significatifs supplémentaires à
inclure quand une valeur à virgule flottante est convertie en texte. Avec la valeur par défaut
de 0, la sortie est la même sur chaque plate-forme supportée par PostgreSQL. L'augmenter va
produire une sortie qui représentera de façon plus précise la valeur stockée, mais cela pourrait
la rendre non portable.

144
Types de données

Note
Le paramètre extra_float_digits contrôle le nombre de chiffres significatifs inclus lorsqu'une
valeur à virgule flottante est convertie en texte. Avec la valeur par défaut de 0, la sortie est la
même sur chaque plate-forme supportée par PostgreSQL. L'augmenter va produire une sortie
représentant plus précisément la valeur stockée, mais il est possible que la sortie soit différente
suivant les plates-formes.

En plus des valeurs numériques ordinaires, les types à virgule flottante ont plusieurs valeurs spéciales :

Infinity
-Infinity
NaN

Elles représentent les valeurs spéciales de l'IEEE 754, respectivement « infinity » (NdT : infini),
« negative infinity » (NdT : infini négatif) et « not-a-number » (NdT : pas un nombre) (sur une
machine dont l'arithmétique à virgule flottante ne suit pas l'IEEE 754, ces valeurs ne fonctionnent
probablement pas comme espéré). Lorsqu'elles sont saisies en tant que constantes dans une commande
SQL, ces valeurs doivent être placées entre guillemets. Par exemple, UPDATE table SET x =
'-Infinity'. En entrée, ces valeurs sont reconnues, quelle que soit la casse utilisée.

Note
IEEE754 spécifie que NaN ne devrait pas être considéré égale à toute autre valeur en virgule
flottante (ceci incluant NaN). Pour permettre le tri des valeurs en virgule flottante et leur
utilisation dans des index basés sur des arbres, PostgreSQL traite les valeurs NaN comme
identiques entre elles, mais supérieures à toute valeur différente de NaN.

PostgreSQL autorise aussi la notation float du standard SQL, ainsi que float(p) pour
indiquer des types numériques inexacts. p indique la précision minimale acceptable en chiffres
binaires. PostgreSQL accepte de float(1) à float(24), qu'il transforme en type real, et de
float(25) à float(53), qu'il transforme en type double precision. Toute valeur de p
hors de la zone des valeurs possibles produit une erreur. float sans précision est compris comme
double precision.

Note
L'affirmation que les real et les double precision ont exactement 24 et 53 bits dans
la mantisse est correcte pour les implémentations des nombres à virgule flottante respectant le
standard IEEE. Sur les plates-formes non-IEEE, c'est peut-être un peu sous-estimé, mais, pour
plus de simplicité, la gamme de valeurs pour p est utilisée sur toutes les plates-formes.

8.1.4. Types seriés

Note
Cette section décrit une façon spécifique à PostgreSQL de créer une colonne autoincrémentée.
Une autre façon revient à utiliser les colonnes d'identité, décrite sur CREATE TABLE.

145
Types de données

Les types de données smallserial, serial et bigserial ne sont pas de vrais types, mais
plutôt un raccourci de notation pour créer des colonnes d'identifiants uniques (similaires à la propriété
AUTO_INCREMENT utilisée par d'autres SGBD). Dans la version actuelle, indiquer :

CREATE TABLE nom_de_table (


nom_de_colonne SERIAL
);

est équivalent à écrire :

CREATE SEQUENCE nom_de_table_nom_de_colonne_seq AS integer;


CREATE TABLE nom_de_table (
nom_de_colonne integer NOT NULL DEFAULT
nextval('nom_de_table_nom_de_colonne_seq') NOT NULL
);
ALTER SEQUENCE nom_de_table_nom_de_colonne_seq OWNED
BY nom_de_table.nom_de_colonne;

Ainsi a été créée une colonne d'entiers dont la valeur par défaut est assignée par un générateur de
séquence. Une contrainte NOT NULL est ajoutée pour s'assurer qu'une valeur NULL ne puisse pas
être insérée. (Dans la plupart des cas, une contrainte UNIQUE ou PRIMARY KEY peut être ajoutée
pour interdire que des doublons soient créés par accident, mais ce n'est pas automatique.) Enfin, la
séquence est marquée « owned by » (possédée par) la colonne pour qu'elle soit supprimée si la colonne
ou la table est supprimée.

Note
Comme smallserial, serial et bigserial sont implémentés en utilisant des
séquences, il peut y avoir des trous dans la séquence de valeurs qui apparait dans la colonne,
même si aucune ligne n'est jamais supprimée. Une valeur allouée à partir de la séquence
est toujours utilisée même si la ligne contenant cette valeur n'est pas insérée avec succès
dans la colonne de la table. Cela peut survenir si la transaction d'insertion est annulée. Voir
nextval() dans Section 9.16 pour plus de détails.

Pour insérer la valeur suivante de la séquence dans la colonne serial, il faut préciser que la valeur
par défaut de la colonne doit être utilisée. Cela peut se faire de deux façons : soit en excluant cette
colonne de la liste des colonnes de la commande INSERT, soit en utilisant le mot-clé DEFAULT.

Les types serial et serial4 sont identiques : ils créent tous les deux des colonnes integer. Les
types bigserial et serial8 fonctionnent de la même façon, mais créent des colonnes bigint.
bigserial doit être utilisé si plus de 231 identifiants sont prévus sur la durée de vie de la table.
Les noms de type smallserial et serial2 fonctionnent de la même façon, sauf qu'ils créent une
colonne de type smallint.

La séquence créée pour une colonne serial est automatiquement supprimée quand la colonne
correspondante est supprimée. La séquence peut être détruite sans supprimer la colonne, mais la valeur
par défaut de la colonne est alors également supprimée.

8.2. Types monétaires


Le type money stocke un montant en devise avec un nombre fixe de décimales. Voir le Tableau 8.3. La
précision de la partie fractionnée est déterminée par le paramètre lc_monetary de la base de données.
L'échelle indiquée dans la table suppose qu'il y a deux chiffres dans la partie fractionnée. De nombreux
formats sont acceptés en entrée, dont les entiers et les nombres à virgule flottante, ainsi que les formats
classiques de devises, comme '$1,000.00'. Le format de sortie est généralement dans le dernier
format, mais dépend de la locale.

146
Types de données

Tableau 8.3. Types monétaires


Nom Taille de Description Étendue
stockage
money 8 octets montant monétaire -92233720368547758.08 à
+92233720368547758.07

Comme la sortie de type de données est sensible à la locale, la recharge de données de type money
dans une base de données pourrait ne pas fonctionner si la base a une configuration différente pour
lc_monetary. Pour éviter les problèmes, avant de restaurer une sauvegarde dans une nouvelle base
de données, assurez-vous que lc_monetary a la même valeur ou une valeur équivalente à celle de
la base qui a été sauvegardée.

Les valeurs de types numeric, int et bigint peuvent être converties en type money. La
conversion à partir du type real et double precision peut être faite en convertissant tout
d'abord vers le type numeric. Par exemple :

SELECT '12.34'::float8::numeric::money;

Néanmoins, ce n'est pas recommandé. Les nombres à virgules flottantes ne doivent pas être utilisés
pour gérer de la monnaie à cause des erreurs potentielles d'arrondis.

Une valeur money peut être convertie en numeric sans perdre de précision. Les conversions vers
d'autres types peuvent potentiellement perdre en précision et doivent aussi se faire en deux étapes :

SELECT '52093.89'::money::numeric::float8;

La division d'une valeur de type money par une valeur de type entier est réalisée en tronquant la partie
décimale. Pour obtenir un résultat arrondi, il faut diviser par une valeur en virgule flottante ou convertir
la valeur de type money en numeric avant de réaliser la division. Il faudra ensuite convertir vers le
type money. (Cette dernière méthode est préférable pour éviter de perdre en précision.) Quand une
valeur de type money est divisée par une autre valeur de type money, le résultat est du type double
precision (c'est-à-dire un nombre pur, pas une monnaie). Les unités de monnaie s'annulent dans
la division.

8.3. Types caractère


Tableau 8.4. Types caractère
Nom Description
character varying(n), varchar(n) Longueur variable avec limite
character(n), char(n) longueur fixe, complété par des espaces
text longueur variable illimitée

Le Tableau 8.4 présente les types génériques disponibles dans PostgreSQL.

SQL définit deux types de caractères principaux : character varying(n) et character(n)


où n est un entier positif. Ces deux types permettent de stocker des chaînes de caractères de taille
inférieure ou égale à n (ce ne sont pas des octets). Toute tentative d'insertion d'une chaîne plus longue
conduit à une erreur, à moins que les caractères en excès ne soient tous des espaces, auquel cas la
chaîne est tronquée à la taille maximale (cette exception étrange est imposée par la norme SQL). Si la
chaîne à stocker est plus petite que la taille déclarée, les valeurs de type character sont complétées
par des espaces, celles de type character varying sont stockées en l'état.

147
Types de données

Si une valeur est explicitement transtypée en character varying(n) ou en character(n),


une valeur trop longue est tronquée à n caractères sans qu'aucune erreur ne soit levée (ce comportement
est aussi imposé par la norme SQL.)

Les notations varchar(n) et char(n) sont des alias de character varying(n) et


character(n), respectivement. Si indiqué, la longueur doit être supérieure à zéro et ne peut
pas excéder 10485760. character sans indication de taille est équivalent à character(1). Si
character varying est utilisé sans indicateur de taille, le type accepte des chaînes de toute taille.
Il s'agit là d'une spécificité de PostgreSQL.

De plus, PostgreSQL propose aussi le type text, qui permet de stocker des chaînes de n'importe
quelle taille. Bien que le type text ne soit pas dans le standard SQL, plusieurs autres systèmes de
gestion de bases de données SQL le proposent également.

Les valeurs de type character sont complétées physiquement à l'aide d'espaces pour atteindre la
longueur n indiquée. Ces valeurs sont également stockées et affichées de cette façon. Cependant, les
espaces de remplissage sont traités comme sémantiquement non significatifs et sont donc ignorés lors
de la comparaison de deux valeurs de type character. Dans les collationnements où les espaces de
remplissage sont significatifs, ce comportement peut produire des résultats inattendus, par exemple
SELECT 'a '::CHAR(2) collate "C" < E'a\n'::CHAR(2) retourne vrai, même si
la locale C considérerait qu'un espace est plus grand qu'un retour chariot. Les espaces de remplissage
sont supprimés lors de la conversion d'une valeur character vers l'un des autres types chaîne. Ces
espaces ont une signification sémantique pour les valeurs de type character varying et text,
et lors de l'utilisation de la correspondance de motifs, par exemple avec LIKE ou avec les expressions
rationnelles.

Les caractères pouvant être enregistrés dans chacun de ces types de données sont déterminés par le jeu
de caractères de la base de données, qui a été sélectionné à la création de la base. Quelque soit le jeu
de caractères spécifique, le caractère de code zéro (quelque fois appelé NUL) ne peut être enregistré.
Pour plus d'informations, voir Section 23.3.

L'espace nécessaire pour une chaîne de caractères courte (jusqu'à 126 octets) est de un octet, plus
la taille de la chaîne qui inclut le remplissage avec des espaces dans le cas du type character.
Les chaînes plus longues ont quatre octets d'en-tête au lieu d'un seul. Les chaînes longues sont
automatiquement compressées par le système, donc le besoin pourrait être moindre. Les chaînes
vraiment très longues sont stockées dans des tables supplémentaires, pour qu'elles n'empêchent pas
d'accéder rapidement à des valeurs plus courtes. Dans tous les cas, la taille maximale possible pour une
chaîne de caractères est de l'ordre de 1 Go. (La taille maximale pour n dans la déclaration de type est
inférieure. Il ne sert à rien de modifier ce comportement, car avec les encodages sur plusieurs octets,
les nombres de caractères et d'octets peuvent être très différents. Pour stocker de longues chaînes sans
limite supérieure précise, il est préférable d'utiliser les types text et character varying sans
taille, plutôt que d'indiquer une limite de taille arbitraire.)

Astuce
Il n'y a aucune différence de performance parmi ces trois types, si ce n'est la place disque
supplémentaire requise pour le type à remplissage et quelques cycles CPU supplémentaires
pour vérifier la longueur lors du stockage dans une colonne contrainte par la taille. Bien que
character(n) ait des avantages en termes de performance sur certains autres systèmes
de bases de données, il ne dispose pas de ce type d'avantages dans PostgreSQL ; en fait,
character(n) est habituellement le plus lent des trois à cause des coûts de stockage
supplémentaires. Dans la plupart des situations, les types text et character varying
peuvent être utilisés à leur place.

On peut se référer à la Section 4.1.2.1 pour obtenir plus d'informations sur la syntaxe des libellés de
chaînes, et le Chapitre 9 pour des informations complémentaires sur les opérateurs et les fonctions.

148
Types de données

Exemple 8.1. Utilisation des types caractère


CREATE TABLE test1 (a character(4));
INSERT INTO test1 VALUES ('ok');
SELECT a, char_length(a) FROM test1; -- 1

a | char_length
------+-------------
ok | 2

CREATE TABLE test2 (b varchar(5));


INSERT INTO test2 VALUES ('ok');
INSERT INTO test2 VALUES ('bien ');
INSERT INTO test2 VALUES ('trop long');
ERROR: value too long for type character varying(5)
INSERT INTO test2 VALUES ('trop long'::varchar(5)); -- troncature
explicite
SELECT b, char_length(b) FROM test2;

b | char_length
-------+-------------
ok | 2
bien | 5
trop | 5

1 La fonction char_length est décrite dans la Section 9.4.

Il y a deux autres types caractère de taille fixe dans PostgreSQL. Ils sont décrits dans le Tableau 8.5.
Le type name existe uniquement pour le stockage des identifiants dans les catalogues système et n'est
pas destiné à être utilisé par les utilisateurs normaux. Sa taille est actuellement définie à 64 octets (63
utilisables plus le terminateur), mais doit être référencée en utilisant la constante NAMEDATALEN en
code source C. La taille est définie à la compilation (et est donc ajustable pour des besoins particuliers).
La taille maximale par défaut peut éventuellement être modifiée dans une prochaine version. Le type
"char" (attention aux guillemets) est différent de char(1), car il n'utilise qu'un seul octet de
stockage. Il est utilisé dans les catalogues système comme un type d'énumération simpliste.

Tableau 8.5. Types caractères spéciaux


Nom Taille de stockage Description
"char" 1 octet type interne d'un octet
name 64 octets type interne pour les noms d'objets

8.4. Types de données binaires


Le type de données bytea permet de stocker des chaînes binaires ; voir le Tableau 8.6.

Tableau 8.6. Types de données binaires


Nom Espace de stockage Description
bytea un à quatre octets plus Chaîne binaire de longueur variable
la taille de la chaîne
binaire à stocker

Une chaîne binaire est une séquence d'octets. Les chaînes binaires se distinguent des chaînes de
caractères de deux façons : tout d'abord, les chaînes binaires permettent de stocker des octets de

149
Types de données

valeurs zéro ainsi que les autres caractères « non imprimables » (habituellement, les octets en dehors
de l'intervalle décimal de 32 à 126). Les chaînes de caractères interdisent les octets de valeur zéro et
interdisent aussi toute valeur d'octet ou séquence d'octets invalide selon l'encodage sélectionné pour
la base de données. Ensuite, les opérations sur les chaînes binaires traitent réellement les octets alors
que le traitement de chaînes de caractères dépend de la configuration de la locale. En résumé, les
chaînes binaires sont appropriées pour le stockage de données que le développeur considère comme
des « octets bruts », alors que les chaînes de caractères sont appropriées pour le stockage de texte.

Le type bytea accepte deux formats en entrée et en sortie le format « hex » et le format historique de
PostgreSQL, « escape ». Les deux sont acceptés en entrée. Le format de sortie dépend du paramètre
de configuration bytea_output ; ce dernier sélectionne par défaut le format hexadécimal. (Notez que le
format hexadécimal est disponible depuis PostgreSQL 9.0 ; les versions antérieures et certains outils
ne le comprennent pas.)

Le standard SQL définit un type de chaîne binaire différent, appelé BLOB ou BINARY LARGE
OBJECT. Le format en entrée est différent du bytea, mais les fonctions et opérateurs fournis sont
pratiquement les mêmes.

8.4.1. Le format hexadécimal bytea


Le format « hex » code les données binaires sous la forme de deux chiffres hexadécimaux par octet,
le plus significatif en premier. La chaîne complète est précédée par la séquence \x (pour la distinguer
du format d'échappement). Dans certains cas, l'antislash initial peut avoir besoin d'être échappé par
un doublage du caractère (voir Section 4.1.2.1). En saisie, les chiffres hexadécimaux peuvent être soit
en majuscules, soit en minuscules, et les espaces blancs sont permis entre les paires de chiffres (mais
pas à l'intérieur d'une paire ni dans la séquence \x de début). Le format hexadécimal est compatible
avec une grande variété d'applications et de protocoles externes, et il a tendance à être plus rapide à
convertir que le format d'échappement. Son utilisation est donc préférée.

Exemple :

SET bytea_output = 'hex';

SELECT '\xDEADBEEF'::bytea;
bytea
------------
\xdeadbeef

8.4.2. Le format d'échappement bytea


Le format d'échappement (« escape ») est le format traditionnel de PostgreSQL pour le type bytea.
Son approche est de représenter une chaîne binaire comme un séquence de caractères ASCII et
de convertir les données qui ne peuvent pas être représentées en ASCII en une séquence spéciale
d'échappement. Si, du point de vue de l'application, représenter les octets sous la forme de caractères
revêt un sens, alors cette représentation est intéressante. En pratique, c'est généralement source de
confusion, car cela diminue la distinction entre chaînes binaires et chaînes textuelles. De plus, le
mécanisme particulier de l'échappement qui a été choisi est quelque peu complexe. Donc ce format
devrait probablement être évité pour la plupart des nouvelles applications.

Lors de la saisie de valeurs bytea dans le format d'échappement, les octets de certaines valeurs
doivent être échappés alors que les autres valeurs d'octets peuvent être échappés. En général, pour
échapper un octet, il suffit de le convertir dans sa valeur octale composée de trois chiffres et de la faire
précéder d'un antislash (ou de deux antislashs s'il faut utiliser la syntaxe d'échappement de chaînes).
L'antislash lui-même (octet en valeur décimal, 92) peut alternativement être représenté par un double
antislash. Le Tableau 8.7 affiche les caractères qui doivent être échappés et donne les séquences
d'échappement possibles.

150
Types de données

Tableau 8.7. Octets littéraux bytea à échapper


Valeur décimale Description Représentation Exemple Représentation
de l'octet échappée en hexadécimale
entrée
0 octet zéro '\000' SELECT \x00
'\000'::bytea;
39 apostrophe '''' ou '\047' SELECT \x27
''''::bytea;
92 antislash '\\' or '\134' SELECT '\ \x5c
\'::bytea;
de 0 à 31 et de 127 octets « non '\xxx' (valeur SELECT \x01
à 255 affichables » octale) '\001'::bytea;

La nécessité d'échapper les octets non affichables dépend des paramétrages de la locale. Il est parfois
possible de s'en sortir sans échappement.

La raison pour laquelle les guillemets simples doivent être doublés, comme indiqué dans Tableau 8.7,
est que cela est vrai pour toute chaîne litérale dans une commande SQL. L'analyseur générique des
chaînes litérales utilise les guillemets simples externes et réduit toute paire de guillemets simples en un
seul caractère. La fonction en entrée du type bytea ne voit qu'un guillemet simple, qu'il traire comme
un caractère standard. Néanmoins, la fonction en entrée du type bytea traite les antislashs de façon
spéciale et les autres comportements montrés dans Tableau 8.7 sont implémentés par cette fonction.

Dans certains contextes, les antislashs doivent être doublés par rapport à ce qui est montré ci-dessus
car l'analyseur générique de chaîne litérale réduira aussi les paires d'antislashs en un seul caractère de
données ; voir Section 4.1.2.1.

Les octets Bytea sont affichés par défaut dans le format hex. Si vous modifiez bytea_output à
escape, les octets « non affichables » sont convertis dans leur équivalent sous la forme d'une valeur
octale à trois chiffres et précédé d'un antislash. La plupart des octets « affichables » sont affichés dans
leur représentation standard pour le jeu de caractères du client :

SET bytea_output = 'escape';

SELECT 'abc \153\154\155 \052\251\124'::bytea;


bytea
----------------
abc klm *\251T

L'octet de valeur décimale 92 (antislash) est doublé en sortie. Les détails sont dans le Tableau 8.8.

Tableau 8.8. Octets échappés en sortie pour bytea


Valeur décimale Description Représentation Exemple Résultat en sortie
de l'octet de sortie
échappée
92 antislash \\ SELECT \\
'\134'::bytea;
0 à 31 et 127 à 255 octets« non \xxx (valeur SELECT \001
affichables » octale) '\001'::bytea;
32 à 126 octets Représentation SELECT ~
« affichables » dans le jeu de '\176'::bytea;
caractères du client

151
Types de données

En fonction de l'interface utilisée pour accéder à PostgreSQL, un travail supplémentaire


d'échappement/de « déséchappement » des chaînes bytea peut être nécessaire. Il faut également
échapper les sauts de lignes et retours à la ligne si l'interface les traduit automatiquement, par exemple.

8.5. Types date/heure


PostgreSQL supporte l'ensemble des types date et heure du SQL. Ces types sont présentés dans le
Tableau 8.9. Les opérations disponibles sur ces types de données sont décrites dans la Section 9.9.
Les dates sont comptées suivant le calendrier grégorien, même dans le cas des dates antérieures à
l'introduction du calendrier (voir) Section B.6 pour plus d'informations).

Tableau 8.9. Types date et heure


Nom Taille de Description Valeur Valeur Résolution
stockage minimale maximale
timestamp 8 octets date et heure 4713 avant JC 294276 après 1 microseconde
[ (p) ] (sans fuseau JC
[ without horaire)
time zone ]
timestamp 8 octets date et heure, 4713 avant JC 294276 après 1 microseconde
[ (p) ] avec fuseau JC
with time horaire
zone
date 4 octets date seule (pas 4713 avant JC 5874897 après 1 jour
d'heure) JC
time 8 octets heure seule 00:00:00.00 24:00:00 1 microseconde
[ (p) ] (pas de date)
[ without
time zone ]
time 12 octets heure (sans 00:00:00+1559 24:00:00-1559 1 microseconde
[ (p) ] date), avec
with time fuseau horaire
zone
interval [ 16 octets intervalles de -178000000 178000000 1 microseconde
champs ] temps années années
[ (p) ]

Note
Le standard SQL impose que timestamp soit un équivalent de timestamp without
time zone. timestamptz est accepté comme abréviation pour timestamp with
time zone ; c'est une extension PostgreSQL.

time, timestamp, et interval acceptent une précision optionnelle p, qui indique le nombre de
décimales pour les secondes. Il n'y a pas, par défaut, de limite explicite à cette précision. Les valeurs
acceptées pour p s'étendent de 0 à 6.

Le type interval a une option supplémentaire, qui permet de restreindre le jeu de champs stockés
en écrivant une de ces expressions :

YEAR
MONTH
DAY

152
Types de données

HOUR
MINUTE
SECOND
YEAR TO MONTH
DAY TO HOUR
DAY TO MINUTE
DAY TO SECOND
HOUR TO MINUTE
HOUR TO SECOND
MINUTE TO SECOND

Notez que si champs et p sont tous les deux indiqués, champs doit inclure SECOND, puisque la
précision s'applique uniquement aux secondes.

Le type time with time zone est défini dans le standard SQL, mais sa définition lui prête des
propriétés qui font douter de son utilité. Dans la plupart des cas, une combinaison de date, time,
timestamp without time zone et timestamp with time zone devrait permettre de
résoudre toutes les fonctionnalités de date et heure nécessaires à une application.

Les types abstime et reltime sont des types de précision moindre, utilisés en interne. Il n'est
pas recommandé de les utiliser dans de nouvelles applications, car ils pourraient disparaître dans une
prochaine version.

8.5.1. Saisie des dates et heures


La saisie de dates et heures peut se faire dans la plupart des formats raisonnables, dont ISO8601,
tout format compatible avec SQL, le format POSTGRES traditionnel ou autres. Pour certains formats,
l'ordre des jours, mois et années en entrée est ambigu. Il est alors possible de préciser l'ordre attendu
pour ces champs. Le paramètre datestyle peut être positionné à MDY pour choisir une interprétation
mois-jour-année, à DMY pour jour-mois-année ou à YMD pour année-mois-jour.

PostgreSQL est plus flexible que la norme SQL ne l'exige pour la manipulation des dates et des heures.
Voir l'Annexe B pour connaître les règles exactes de reconnaissance des dates et heures et les formats
reconnus pour les champs texte comme les mois, les jours de la semaine et les fuseaux horaires.

Tout libellé de date ou heure saisi doit être placé entre apostrophes, comme les chaînes de caractères.
La Section 4.1.2.7 peut être consultée pour plus d'information. SQL requiert la syntaxe suivante :

type [ (p) ] 'valeur'

où p, précision optionnelle, est un entier correspondant au nombre de décimales du champ secondes.


La précision peut être spécifiée pour les types time, timestamp et interval, et peut aller de 0
à 6. Si aucune précision n'est indiquée dans une déclaration de constante, celle de la valeur littérale
est utilisée (mais pas plus de 6 chiffres).

8.5.1.1. Dates
Le Tableau 8.10 regroupe les formats de date possibles pour la saisie de valeurs de type date.

Tableau 8.10. Saisie de date


Exemple Description
1999-01-08 ISO-8601 ; 8 janvier, quel que soit le mode (format recommandé)
January 8, 1999 sans ambiguïté quel que soit le style de date (datestyle)
1/8/1999 8 janvier en mode MDY ; 1er août en mode DMY
1/18/1999 18 janvier en mode MDY ; rejeté dans les autres modes

153
Types de données

Exemple Description
01/02/03 2 janvier 2003 en mode MDY ; 1er février 2003 en mode DMY ; 3 février 2001 en
mode YMD
1999-Jan-08 8 janvier dans tous les modes
Jan-08-1999 8 janvier dans tous les modes
08-Jan-1999 8 janvier dans tous les modes
99-Jan-08 8 janvier en mode YMD, erreur sinon
08-Jan-99 8 janvier, sauf en mode YMD : erreur
Jan-08-99 8 janvier, sauf en mode YMD : erreur
19990108 ISO-8601 ; 8 janvier 1999 dans tous les modes
990108 ISO-8601 ; 8 janvier 1999 dans tous les modes
1999.008 Année et jour de l'année
J2451187 Date du calendrier Julien
January 8, 99 Année 99 avant Jésus Christ
BC

8.5.1.2. Heures
Les types « heure du jour » sont time [ (p) ] without time zone et time [ (p) ]
with time zone. time est équivalent à time without time zone.

Les saisies valides pour ces types sont constituées d'une heure suivie éventuellement d'un fuseau
horaire (voir le Tableau 8.11 et le Tableau 8.12). Si un fuseau est précisé pour le type time without
time zone, il est ignoré sans message d'erreur. Si une date est indiquée, elle est ignorée, sauf si
un fuseau horaire impliquant une règle de changement d'heure (heure d'été/heure d'hiver) est précisé,
America/New_York par exemple. Dans ce cas, la date est nécessaire pour pouvoir déterminer la
règle de calcul de l'heure qui s'applique. Le décalage approprié du fuseau horaire est enregistré dans la
valeur de time with time zone et est affiché de la façon dont il est stocké ; il n'est pas converti
vers le fuseau horaire actif.

Tableau 8.11. Saisie d'heure


Exemple Description
04:05:06.789 ISO 8601
04:05:06 ISO 8601
04:05 ISO 8601
040506 ISO 8601
04:05 AM Identique à 04:05 ; AM n'affecte pas la valeur
04:05 PM Identique à 16:05 ; l'heure doit être <= 12
04:05:06.789-8ISO 8601, avec le décalage UTC comme fuseau horaire
04:05:06-08:00ISO 8601, avec le décalage UTC comme fuseau horaire
04:05-08:00 ISO 8601, avec le décalage UTC comme fuseau horaire
040506+0730 ISO 8601, avec le décalage UTC avec un fuseau horaire en heure fractionnée
décalage UTC exprimé en secondes (non autorisé dans ISO 8601)
040506+07:30:00
040506-08 ISO 8601
04:05:06 PST fuseau horaire abrégé
2003-04-12 fuseau horaire en nom complet
04:05:06

154
Types de données

Exemple Description
America/
New_York

Tableau 8.12. Saisie des fuseaux horaires


Exemple Description
PST Abréviation pour l'heure standard du Pacifique (Pacific Standard Time)
America/ Nom complet du fuseau horaire
New_York
PST8PDT Nommage POSIX du fuseau horaire
-8:00:00 Décalage UTC pour la zone PST
-8:00 Décalage ISO-8601 pour la zone PST (format étendu ISO 8601)
-800 Décalage ISO-8601 pour la zone PST (format basique ISO 8601)
-8 Décalage ISO-8601 pour la zone PST (format basique ISO 8601)
zulu Abréviation militaire de GMT
z Version courte de zulu (aussi dans ISO 8601)

La Section 8.5.3 apporte des précisions quant à la façon d'indiquer les fuseaux horaires.

8.5.1.3. Horodatage
Les saisies valides sont constituées de la concaténation d'une date et d'une heure, éventuellement suivie
d'un fuseau horaire et d'un qualificatif AD (après Jésus Christ) ou BC (avant Jésus Christ). (AD/BC peut
aussi apparaître avant le fuseau horaire, mais ce n'est pas l'ordre préféré.) Ainsi :

1999-01-08 04:05:06

et :

1999-01-08 04:05:06 -8:00

sont des valeurs valides, qui suivent le standard ISO 8601. Le format très courant :

January 8 04:05:06 1999 PST

est également supporté.

Le standard SQL différencie les libellés timestamp without time zone et timestamp
with time zone par la présence d'un symbole « + » ou d'un « - » et le décalage du fuseau horaire
après l'indication du temps. De ce fait, d'après le standard,

TIMESTAMP '2004-10-19 10:23:54'

est du type timestamp without time zone alors que

TIMESTAMP '2004-10-19 10:23:54+02'

est du type timestamp with time zone. PostgreSQL n'examine jamais le contenu d'un
libellé avant de déterminer son type. Du coup, il traite les deux ci-dessus comme des valeurs de type
timestamp without time zone. Pour s'assurer qu'un littéral est traité comme une valeur de
type timestamp with time zone, il faut préciser explicitement le bon type :

TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'

155
Types de données

Dans un libellé de type timestamp without time zone, PostgreSQL ignore silencieusement
toute indication de fuseau horaire. C'est-à-dire que la valeur résultante est dérivée des champs date/
heure de la valeur saisie et n'est pas corrigée par le fuseau horaire.

Pour timestamp with time zone, la valeur stockée en interne est toujours en UTC (Universal
Coordinated Time ou Temps Universel Coordonné), aussi connu sous le nom de GMT (Greenwich
Mean Time). Les valeurs saisies avec un fuseau horaire explicite sont converties en UTC à l'aide du
décalage approprié. Si aucun fuseau horaire n'est précisé, alors le système considère que la date est
dans le fuseau horaire indiqué par le paramètre système TimeZone, et la convertit en UTC en utilisant
le décalage de la zone timezone.

Quand une valeur timestamp with time zone est affichée, elle est toujours convertie de
l'UTC vers le fuseau horaire courant (variable timezone), et affichée comme une heure locale. Pour
voir l'heure dans un autre fuseau horaire, il faut, soit changer la valeur de timezone, soit utiliser la
construction AT TIME ZONE (voir la Section 9.9.3).

Les conversions entre timestamp without time zone et timestamp with time zone
considèrent normalement que la valeur timestamp without time zone utilise le fuseau
horaire timezone. Un fuseau différent peut être choisi en utilisant AT TIME ZONE.

8.5.1.4. Valeurs spéciales


PostgreSQL supporte plusieurs valeurs de dates spéciales, dans un souci de simplification. Ces valeurs
sont présentées dans le Tableau 8.13. Les valeurs infinity et -infinity ont une représentation
spéciale dans le système et sont affichées ainsi ; les autres ne sont que des raccourcies de notation
convertis en dates/heures ordinaires lorsqu'ils sont lus. (En particulier, now et les chaînes relatives sont
converties en une valeur de temps spécifique à leur lecture). Toutes ces valeurs doivent être écrites
entre simples quotes lorsqu'elles sont utilisées comme des constantes dans les commandes SQL.

Tableau 8.13. Saisie de dates/heures spéciales


Saisie Types valides Description
epoch date, timestamp 1970-01-01 00:00:00+00 (date
système zéro d'Unix)
infinity date, timestamp plus tard que toutes les autres
dates
-infinity date, timestamp plus tôt que toutes les autres
dates
now date, time, timestamp heure de démarrage de la
transaction courante
today date, timestamp aujourd'hui minuit (00:00)
tomorrow date, timestamp demain minuit (00:00)
yesterday date, timestamp hier minuit (00:00)
allballs time 00:00:00.00 UTC

Les fonctions suivantes, compatibles avec le standard SQL, peuvent aussi être utilisées pour obtenir
l'heure courante pour le type de données correspondant : CURRENT_DATE, CURRENT_TIME,
CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. (Voir la Section 9.9.4). Ce sont là des
fonctions SQL qui ne sont pas reconnues comme chaînes de saisie de données.

Attention
Bien qu'il n'y ait pas de problèmes à utiliser les chaînes now, today, tomorrow et
yesterday dans des commandes SQL interactives, elles peuvent avoir un comportement
surprenant quand la commande est sauvegardée pour une exécution ultérieure, par exemple

156
Types de données

dans des requêtes préparées, des vues ou des fonctions. La chaîne peut être convertie en une
valeur spécifique qui continue à être utilisée bien après qu'elle ne soit obsolète. Dans de tels
contextes, utilisez plutôt une des fonctions SQL. Par exemple, CURRENT_DATE + 1 est
plus sûr que 'tomorrow'::date.

8.5.2. Affichage des dates et heures


Le format de sortie des types date/heure peut être positionné à l'un des quatre formats de date
suivants : ISO 8601, SQL (Ingres), traditionnel POSTGRES (date au format Unix date) ou German
(germanique). Le format par défaut est le format ISO. (Le standard SQL impose l'utilisation du
format ISO 8601. Le nom du format d'affichage « SQL » est mal choisi, un accident historique.)
Le Tableau 8.14 présente des exemples de chaque format d'affichage. La sortie d'un type date ou
time n'est évidemment composée que de la partie date ou heure, comme montré dans les exemples.
Néanmoins, le style POSTGRES affiche seulement les dates dans le format ISO.

Tableau 8.14. Styles d'affichage de date/heure


Spécification de style Description Exemple
ISO standard SQL ISO 8601 1997-12-17
07:37:16-08
SQL style traditionnel 12/17/1997
07:37:16.00 PST
Postgres style original Wed Dec 17 07:37:16
1997 PST
German style régional 17.12.1997
07:37:16.00 PST

Note
ISO 8601 spécifie l'utilisation d'une lettre T en majuscule pour séparer la date et l'heure.
PostgreSQL accepte ce format en entrée. En sortie, il utilise un espace plutôt qu'un T, comme
indiqué ci-dessus. C'est à la fois plus lisible et cohérent avec la RFC 3339 ainsi qu'avec d'autres
systèmes de bases de données.

Dans les styles SQL et POSTGRES, les jours apparaissent avant le mois si l'ordre des champs DMY
a été précisé, sinon les mois apparaissent avant les jours (voir la Section 8.5.1 pour savoir comment
ce paramètre affecte l'interprétation des valeurs en entrée). Le Tableau 8.15 présente des exemples.

Tableau 8.15. Convention de présentation des dates


Valeur de datestyle (style Ordre de saisie Exemple d'affichage
de date)
SQL, DMY jour/mois/année 17/12/1997
15:37:16.00 CET
SQL, MDY mois/jour/année 12/17/1997
07:37:16.00 PST
Postgres, DMY jour/mois/année Wed 17 Dec 07:37:16
1997 PST

Dans le style ISO, le fuseau horaire est toujours affiché sous la forme d'un décalage numérique signé
de UTC, avec un signe positif utilisé pour les zones à l'est de Greenwich. Le décalage sera affiché
sous la forme hh (heures seulement) s'il s'agit d'un nombre intégral d'heures, ou sous la forme hh:mm

157
Types de données

s'il s'agit d'un nombre intégral de minutes, et enfin sous la forme hh:mm:ss. (Le troisième cas n'est
pas possible pour tout standard moderne de fuseau horaire, mais il peut apparaître en travaillant sur
des jours antérieurs à l'adoption des fuseaux horaires standardisés.) Pour les autres styles de dates, le
fuseau horaire est affiché comme une abréviation alphabétique si l'une d'entre elles est d'utilisation
commune dans le fuseau actuel. Sinon, il apparaît comme un décalage numérique signé dans le format
basique ISO 8601 (hh ou hhmm).

Le style de date/heure peut être sélectionné à l'aide de la commande SET datestyle, du paramètre
datestyle du fichier de configuration postgresql.conf ou par la variable d'environnement
PGDATESTYLE sur le serveur ou le client.

La fonction de formatage to_char (voir Section 9.8) permet de formater les affichages de date/heure
de manière plus flexible.

8.5.3. Fuseaux horaires


Les fuseaux horaires et les conventions liées sont influencés par des décisions politiques, pas
uniquement par la géométrie de la Terre. Les fuseaux horaires se sont quelque peu standardisés
au cours du vingtième siècle, mais continuent à être soumis à des changements arbitraires,
particulièrement en respect des règles de changement d'heure (heure d'été/heure d'hiver). PostgreSQL
utilise la très répandue base de données de fuseaux horaires IANA (Olson) pour gérer les informations
sur les règles historiques de fuseau horaire. Pour les dates se situant dans le futur, PostgreSQL part de
l'assomption que les dernières règles connues pour un fuseau continueront à s'appliquer dans le futur.

PostgreSQL se veut compatible avec les définitions standard SQL pour un usage typique. Néanmoins,
le standard SQL possède un mélange étrange de types de date/heure et de possibilités. Deux problèmes
évidents sont :

• bien que le type date ne puisse pas se voir associer un fuseau horaire, le type heure peut en avoir
un. Les fuseaux horaires, dans le monde réel, ne peuvent avoir de sens qu'associés à une date et à
une heure, vu que l'écart peut varier avec l'heure d'été ;

• le fuseau horaire par défaut est précisé comme un écart numérique constant avec l'UTC. Il n'est, de
ce fait, pas possible de s'adapter à l'heure d'été ou d'hiver lorsque l'on fait des calculs arithmétiques
qui passent les limites de l'heure d'été et de l'heure d'hiver.

Pour éviter ces difficultés, il est recommandé d'utiliser des types date/heure qui contiennent à la fois
une date et une heure lorsque les fuseaux horaires sont utilisés. Il est également préférable de ne pas
utiliser le type time with time zone. (Ce type est néanmoins proposé par PostgreSQL pour les
applications existantes et pour assurer la compatibilité avec le standard SQL.) PostgreSQL utilise le
fuseau horaire local pour tous les types qui ne contiennent qu'une date ou une heure.

Toutes les dates et heures liées à un fuseau horaire sont stockées en interne en UTC. Elles sont
converties en heure locale dans le fuseau indiqué par le paramètre de configuration TimeZone avant
d'être affichées sur le client.

PostgreSQL permet d'indiquer les fuseaux horaires de trois façons différentes :

• un nom complet de fuseau horaire, par exemple America/New_York. Les noms reconnus de
fuseau horaire sont listés dans la vue pg_timezone_names (voir Section 52.90). PostgreSQL
utilise les données IANA pour cela, les mêmes noms sont donc reconnus par de nombreux autres
logiciels ;

• une abréviation de fuseau horaire, par exemple PST. Une telle indication ne définit qu'un décalage
particulier à partir d'UTC, en contraste avec les noms complets de fuseau horaire qui peuvent aussi
impliquer un ensemble de dates pour le changement d'heure. Les abréviations reconnues sont listées
dans la vue pg_timezone_abbrevs (voir Section 52.89). Les paramètres de configuration
TimeZone et log_timezone ne peuvent pas être configurés à l'aide d'une abréviation de fuseau
horaire, mais ces abréviations peuvent être utilisées dans les saisies de date/heure et avec l'opérateur
AT TIME ZONE ;

158
Types de données

• En plus des noms et abréviations des fuseaux horaires, PostgreSQL accepte les spécifications de
fuseau horaire du style POSIX, comme décrit dans Section B.5. Cette option n'est habituellement
pas préférable à utiliser un nom de fuseau horaire, mais cela pourrait se révéler nécessaire si aucune
entrée adéquate de fuseau horaire n'est disponible dans la base IANA.

Les abréviations représentent un décalage spécifique depuis UTC, alors qu'un grand nombre des noms
complets implique une règle de changement d'heure, et donc potentiellement deux décalages UTC.
Par exemple, 2014-06-04 12:00 America/New_York représente minuit à New York, ce
qui, pour cette date particulière, sera le fuseau Eastern Daylight Time (UTC-4). Donc 2014-06-04
12:00 EDT stipule ce moment précis. Mais 2014-06-04 12:00 EST représente minuit pour le
fuseau Eastern Standard Time (UTC-5), quel que soit le changement d'heure en effet à cette date.

Pour compliquer encore plus, certaines juridictions ont utilisé les mêmes abréviations de fuseau
horaire pour signifier des décalages UTC différents. Par exemple, Moscow MSK correspondait à UTC
+3 certaines années et UTC+4 à d'autres. PostgreSQL interprète ces abréviations suivant ce à quoi
elles correspondent (ou ont correspondu récemment) pour la date indiquée. Mais, comme le montre
l'exemple EST ci-dessus, ce n'est pas nécessairement la même chose que l'heure civile locale à ce
moment.

Dans tous les cas, les noms et les abréviations des fuseaux horaires sont insensibles à la casse. (C'est
un changement par rapport aux versions de PostgreSQL antérieures à la 8.2 qui étaient sensibles à la
casse dans certains cas et pas dans d'autres.)

Ni les noms ni les abréviations des fuseaux horaires ne sont codés en dur dans le serveur ; ils sont
obtenus à partir des fichiers de configuration stockés sous .../share/timezone/ et .../
share/timezonesets/ du répertoire d'installation (voir Section B.4).

Le paramètre de configuration TimeZone peut être fixé dans le fichier postgresql.conf ou par
tout autre moyen standard décrit dans le Chapitre 19. Il existe aussi quelques manières spéciales de
le configurer :

• la commande SQL SET TIME ZONE configure le fuseau horaire pour une session. C'est une autre
façon d'indiquer SET TIMEZONE TO avec une syntaxe plus compatible avec les spécifications
SQL ;

• la variable d'environnement PGTZ est utilisée par les applications clientes fondées sur libpq pour
envoyer une commande SET TIME ZONE au serveur lors de la connexion.

8.5.4. Saisie d'intervalle


Les valeurs de type interval peuvent être saisies en utilisant la syntaxe verbeuse suivante :

[@] quantité
unité [quantité
unité...]
[direction]

où quantité est un nombre (éventuellement signé) ; unité est microsecond millisecond,


second, minute, hour, day, week, month, year, decade, century, millennium, ou
des abréviations ou pluriels de ces unités ; direction peut être ago (pour indiquer un intervalle
négatif) ou vide. Le signe @ est du bruit optionnel. Les quantités de chaque unité différente sont
implicitement ajoutées, avec prise en compte appropriée des signes (+ et -). ago inverse tous les
champs. Cette syntaxe est aussi utilisée pour les sorties d'intervalles, si IntervalStyle est positionné
à postgres_verbose.

Les quantités de jours, heures, minutes et secondes peuvent être spécifiées sans notations explicites
d'unités. Par exemple '1 12:59:10' est comprise comme '1 day 12 hours 59 min

159
Types de données

10 sec'. Par ailleurs, une combinaison d'années et de mois peut être spécifiée avec un tiret ; par
exemple, '200-10' est compris comme '200 years 10 months'. (Ces formes raccourcies
sont en fait les seules autorisées par le standard SQL, et sont utilisées pour la sortie quand la variable
IntervalStyle est positionnée à sql_standard.)

Les valeurs d'intervalles peuvent aussi être écrites en tant qu'intervalles de temps ISO 8601, en utilisant
soit le « format avec désignateurs » de la section 4.4.3.2 ou le « format alternatif » de la section 4.4.3.3.
Le format avec désignateurs ressemble à ceci :

P quantité unité [ quantité unité ...] [ T [ quantité unité ...]]

La chaîne doit commencer avec un P, et peut inclure un T qui introduit les unités de ce type. Les
abréviations d'unité disponibles sont données dans Tableau 8.16. Des unités peuvent être omises,
et peuvent être spécifiées dans n'importe quel ordre, mais les unités inférieures à un jour doivent
apparaître après T. En particulier, la signification de M dépend de son emplacement, c'est-à-dire avant
ou après T.

Tableau 8.16. Abréviations d'unités d'intervalle ISO 8601


Abréviation Signification
Y Années
M Mois (dans la zone de date)
W Semaines
D Jours
H Heures
M Minutes (dans la zone de temps)
S Secondes

Dans le format alternatif :

P [ années-mois-jours ] [ T heures:minutes:secondes ]

la chaîne doit commencer par P, et un T sépare la zone de date et la zone de temps de l'intervalle. Les
valeurs sont données comme des nombres, de façon similaire aux dates ISO 8601.

Lors de l'écriture d'une constante d'intervalle avec une spécification de champs, ou lors de
l'assignation d'une chaîne à une colonne d'intervalle qui a été définie avec une spécification de
champs, l'interprétation de quantité sans unité dépend des champs. Par exemple, INTERVAL '1'
YEAR est interprété comme 1 an, alors que INTERVAL '1' est interprété comme 1 seconde. De
plus, les valeurs du champ « à droite » du champ le moins significatif autorisé par la spécification de
champs sont annulées de façon silencieuse. Par exemple, écrire INTERVAL '1 day 2:03:04'
HOUR TO MINUTE implique la suppression du champ des secondes, mais pas celui des journées.

D'après le standard SQL, toutes les valeurs de tous les champs d'un intervalle doivent avoir le
même signe, ce qui entraîne qu'un signe négatif initial s'applique à tous les champs ; par exemple,
le signe négatif dans l'expression d'intervalle '-1 2:03:04' s'applique à la fois aux jours et
aux heures/minutes/secondes. PostgreSQL permet que les champs aient des signes différents, et
traditionnellement traite chaque champ de la représentation textuelle comme indépendamment signé,
ce qui fait que la partie heure/minute/seconde est considérée comme positive dans l'exemple. Si
IntervalStyle est positionné à sql_standard, alors un signe initial est considéré comme
s'appliquant à tous les champs (mais seulement si aucun autre signe n'apparaît). Sinon, l'interprétation
traditionnelle de PostgreSQL est utilisée. Pour éviter les ambiguïtés, il est recommandé d'attacher un
signe explicite à chaque partie, si au moins un champ est négatif.

160
Types de données

Les valeurs des champs peuvent avoir des parties fractionnelles : par exemple, '1.5 weeks' ou
'01:02:03.45'. Néanmoins, comme l'intervalle stocke en interne seulement les trois unités sous
forme d'entier (mois, jours, microsecondes), les unités fractionelles doivent être divisées en plus petites
unités. Les parties fractionnelles des unités supérieures aux mois est tronquées en un nombre entier
de mois, par exemple '1.5 years' devient '1 year 6 mons'. Les parties fractionnelles des
semaines et jours sont calculées comme un nombre entier de jours et de microsecondes, en supposant
30 jours par mois et 24 heures par jour, par exemple '1.75 months' devient 1 mon 22 days
12:00:00. Seules les secondes seront affichées en fractionné en sortie.

Tableau 8.17 présente des exemples de saisies d'interval valides.

Tableau 8.17. Saisie d'intervalle


Exemple Description
1-2 Format SQL standard : 1 an 2 mois
3 4:05:06 Format SQL standard : 3 jours 4 heures 5 minutes
6 secondes
1 year 2 months 3 days 4 hours 5 minutes 6 Format PostgreSQL traditionnel : 1 an 2 mois 3
seconds jours 4 heures 5 minutes 6 secondes
P1Y2M3DT4H5M6S « format avec désignateurs » ISO 8601 :
signification identique à ci-dessus
P0001-02-03T04:05:06 « format alternatif » ISO 8601 : signification
identique à ci-dessus

En interne, les valeurs interval sont enregistrées comme des mois, jours et microsecondes. C'est
fait ainsi parce que le nombre de jours dans un mois varie, et un jour peut avoir 23 ou 25 heures s'il
y a eu un changement d'heure. Les champs mois et jours sont des entiers, alors que le champ des
microsecondes peut contenir des secondes fractionnelles. Comme les intervalles sont habituellement
créés à partir de chaînes constantes ou de soustractions de timestamp, cette méthode de stockage
fonctionne bien dans la plupart des cas, mais peut être la cause de résultats inattendus :

SELECT EXTRACT(hours from '80 minutes'::interval);


date_part
-----------
1

SELECT EXTRACT(days from '80 hours'::interval);


date_part
-----------
0

Les fonctions justify_days et justify_hours sont disponibles pour ajuster les jours et heures
qui dépassent l'étendue normale.

8.5.5. Affichage d'intervalles


Le format de sortie du type interval peut être positionné à une de ces quatre valeurs :
sql_standard, postgres, postgres_verbose ou iso_8601, en utilisant la commande
SET intervalstyle. La valeur par défaut est le format postgres. Tableau 8.18 donne des
exemples de chaque style de format de sortie.

Le style sql_standard produit une sortie qui se conforme à la spécification du standard SQL pour
les chaînes littérales d'intervalle, si la valeur de l'intervalle reste dans les restrictions du standard (soit
année-mois seul, ou jour-temps seul, et sans mélanger les composants positifs et négatifs). Sinon, la

161
Types de données

sortie ressemble au standard littéral année-mois suivi par une chaîne jour-temps littérale, avec des
signes explicites ajoutés pour désambiguer les intervalles dont les signes seraient mélangés.

La sortie du style postgres correspond à la sortie des versions de PostgreSQL précédant la 8.4, si
le paramètre datestyle était positionné à ISO.

La sortie du style postgres_verbose correspond à la sortie des versions de PostgreSQL précédant


la 8.4, si le paramètre datestyle était positionné à autre chose que ISO.

La sortie du style iso_8601 correspond au « format avec designateurs » décrit dans la section 4.4.3.2
du standard ISO 8601.

Tableau 8.18. Exemples de styles d'affichage d'intervalles


Spécification de style Intervalle année-mois Intervalle date-temps Interval Mixte
sql_standard 1-2 3 4:05:06 -1-2 +3 -4:05:06
postgres 1 year 2 mons 3 days 04:05:06 -1 year -2 mons +3 days
-04:05:06
postgres_verbose @ 1 year 2 mons @ 3 days 4 hours 5 mins @ 1 year 2 mons -3 days
6 secs 4 hours 5 mins 6 secs
ago
iso_8601 P1Y2M P3DT4H5M6S P-1Y-2M3DT-4H-5M-6S

8.6. Type booléen


PostgreSQL fournit le type boolean du standard SQL ; voir Tableau 8.19. Ce type dispose de
plusieurs états :« true » (vrai), « false » (faux) et un troisième état, « unknown » (inconnu), qui est
représenté par la valeur SQL NULL.

Tableau 8.19. Type de données booléen


Nom Taille du stockage Description
boolean 1 octet état vrai ou faux

Les constantes booléennes peuvent être représentées dans les requêtes SQL avec les mots clés SQL
TRUE, FALSE et NULL.

La fonction en entrée pour le type boolean accepte ces représentations, sous forme de chaîne de
caractères, pour l'état « true » :

true
yes
on
1

et ces représentations pour l'état « false » :

false
no
off
0

Les préfixes uniques de ces chaînes sont aussi acceptés, par exemple t ou n. Les espaces avant ou
après, ainsi que la casse, sont ignorés.

La fonction en sortie pour le boolean renvoie toujours soit t soit f, comme indiqué dans
Exemple 8.2.

162
Types de données

Exemple 8.2. Utilisation du type boolean.


CREATE TABLE test1 (a boolean, b text);
INSERT INTO test1 VALUES (TRUE, 'sic est');
INSERT INTO test1 VALUES (FALSE, 'non est');
SELECT * FROM test1;
a | b
---+---------
t | sic est
f | non est

SELECT * FROM test1 WHERE a;


a | b
---+---------
t | sic est

Les mots clés TRUE et FALSE sont la méthode préférée (compatible SQL) pour l'écriture des
constantes booléennes dans les requêtes SQL. Cependant, vous pouvez aussi utiliser les représentations
sous forme de chaîne de caractères en suivant la syntaxe générique décrite dans Section 4.1.2.7, par
exemple 'yes'::boolean.

Notez que l'analyseur comprend automatiquement que TRUE et FALSE sont du type boolean, mais
ce n'est pas le cas pour NULL car il peut avoir tout type. Donc, dans certains contextes, vous devrez
convertir explicitement NULL vers le type boolean, par exemple NULL::boolean. À l'inverse, la
conversion peut être omise d'une valeur booléenne représentée sous la forme d'une chaîne de caractères
dans les contextes où l'analyseur peut déduire que la constante doit être de type boolean.

8.7. Types énumération


Les types énumérés (enum) sont des types de données qui comprennent un ensemble statique, prédéfini
de valeurs dans un ordre spécifique. Ils sont équivalents aux types enum dans de nombreux langages de
programmation. Les jours de la semaine ou un ensemble de valeurs de statut pour un type de données
sont de bons exemples de type enum.

8.7.1. Déclaration de types énumérés


Les types enum sont créés en utilisant la commande CREATE TYPE. Par exemple :

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

Une fois créé, le type enum peut être utilisé dans des définitions de table et de fonction, comme tous
les autres types :

CREATE TYPE humeur AS ENUM ('triste', 'ok', 'heureux');


CREATE TABLE personne (
nom text,
humeur_actuelle humeur
);
INSERT INTO personne VALUES ('Moe', 'heureux');
SELECT * FROM personne WHERE humeur_actuelle = 'heureux';
name | humeur_actuelle
------+-----------------
Moe | heureux
(1 row)

163
Types de données

8.7.2. Tri
L'ordre des valeurs dans un type enum correspond à l'ordre dans lequel les valeurs sont créées lors de
la déclaration du type. Tous les opérateurs de comparaison et les fonctions d'agrégats relatives peuvent
être utilisés avec des types enum. Par exemple :

INSERT INTO personne VALUES ('Larry', 'triste');


INSERT INTO personne VALUES ('Curly', 'ok');
SELECT * FROM personne WHERE humeur_actuelle > 'triste';
nom | humeur_actuelle
-------+-----------------
Moe | heureux
Curly | ok
(2 rows)

SELECT * FROM personne WHERE humeur_actuelle > 'triste' ORDER BY


humeur_actuelle;
nom | humeur_actuelle
-------+--------------
Curly | ok
Moe | heureux
(2 rows)

SELECT nom
FROM personne
WHERE humeur_actuelle = (SELECT MIN(humeur_actuelle) FROM
personne);
nom
-------
Larry
(1 row)

8.7.3. Sûreté du type


Chaque type de données énuméré est séparé et ne peut pas être comparé aux autres types énumérés.
Par exemple :

CREATE TYPE niveau_de_joie AS ENUM ('heureux', 'très heureux',


'ecstatique');
CREATE TABLE vacances (
nombre_de_semaines integer,
niveau_de_joie niveau_de_joie
);
INSERT INTO vacances(nombre_de_semaines,niveau_de_joie) VALUES (4,
'heureux');
INSERT INTO vacances(nombre_de_semaines,niveau_de_joie) VALUES (6,
'très heureux');
INSERT INTO vacances(nombre_de_semaines,niveau_de_joie) VALUES (8,
'ecstatique');
INSERT INTO vacances(nombre_de_semaines,niveau_de_joie) VALUES (2,
'triste');
ERROR: invalid input value for enum niveau_de_joie: "triste"
SELECT personne.nom, vacances.nombre_de_semaines FROM personne,
vacances
WHERE personne.humeur_actuelle = vacances.niveau_de_joie;

164
Types de données

ERROR: operator does not exist: humeur = niveau_de_joie

Si vous avez vraiment besoin de ce type de conversion, vous pouvez soit écrire un opérateur
personnalisé soit ajouter des conversions explicites dans votre requête :

SELECT personne.nom, vacances.nombre_de_semaines FROM personne,


vacances
WHERE personne.humeur_actuelle::text =
vacances.niveau_de_joie::text;
nom | nombre_de_semaines
------+--------------------
Moe | 4
(1 row)

8.7.4. Détails d'implémentation


Les labels enum sont sensibles à la casse, donc 'heureux' n'est pas identique à 'HEUREUX'. Les
espaces blancs dans les labels sont aussi pris en compte.

Bien que les types enum aient principalement pour but d'être des ensembles statiques de valeurs, il est
possible d'ajouter de nouvelles valeurs à un type enum existant et de renommer les valeurs existantes
(voir ALTER TYPE). Les valeurs existantes ne peuvent pas être supprimées d'un type enum, pas plus
qu'il n'est possible de modifier l'ordre de tri de ces valeurs, si ce n'est en supprimant puis en re- créant
le type enum.

Une valeur enum occupe quatre octets sur disque. La longueur du label texte d'une valeur enum est
limité au paramètre NAMEDATALEN codé en dur dans PostgreSQL ; dans les constructions standard,
cela signifie un maximum de 63 octets.

Les traductions des valeurs enum internes vers des labels texte sont gardées dans le catalogue système
pg_enum. Interroger ce catalogue directement peut s'avérer utile.

8.8. Types géométriques


Les types de données géométriques représentent des objets à deux dimensions. Le Tableau 8.20 liste
les types disponibles dans PostgreSQL.

Tableau 8.20. Types géométriques


Nom Taille de Description Représentation
stockage
point 16 octets Point du plan (x,y)
line 32 octets Ligne infinie ((x1,y1),(x2,y2))
lseg 32 octets Segment de droite fini ((x1,y1),(x2,y2))
box 32 octets Boîte rectangulaire ((x1,y1),(x2,y2))
path 16+16n octets Chemin fermé (similaire à un ((x1,y1),...)
polygone)
path 16+16n octets Chemin ouvert [(x1,y1),...]
polygon 40+16n octets Polygone (similaire à un ((x1,y1),...)
chemin fermé)
circle 24 octets Cercle <(x,y),r> (point central et
rayon)

165
Types de données

Un large ensemble de fonctions et d'opérateurs permettent d'effectuer différentes opérations


géométriques, comme l'échelonnage, la translation, la rotation, la détermination des intersections. Elles
sont expliquées dans la Section 9.11.

8.8.1. Points
Les points sont les briques fondamentales des types géométriques. Les valeurs de type point sont
indiquées à l'aide d'une des syntaxes suivantes :

( x , y )
x , y

où x et y sont les coordonnées respectives sous forme de nombre à virgule flottante.

Les points sont affichés en utilisant la première syntaxe.

8.8.2. Lines
Les lignes sont représentées par l'équation linéaire Ax + By + C = 0, où A et B ne valent pas zéro tous
les deux. Les valeurs de type line sont fournies et récupérées sous la forme suivante :

{ A, B, C }

Il est également possible d'utiliser n'importe laquelle des formes suivantes pour la saisie :

[ ( x1 , y1 ) , ( x2 , y2 ) ]
( ( x1 , y1 ) , ( x2 , y2 ) )
( x1 , y1 ) , ( x2 , y2 )
x1 , y1 , x2 , y2

où (x1,y1) et (x2,y2) sont deux points différents sur la ligne.

8.8.3. Segments de droite


Les segments de ligne sont représentés par des paires de points qui sont les points finaux du segment.
Les valeurs de type lseg sont précisées en utilisant une des syntaxes suivantes :

[ ( x1 , y1 ) , ( x2 , y2 ) ]
( ( x1 , y1 ) , ( x2 , y2 ) )
( x1 , y1 ) , ( x2 , y2 )
x1 , y1 , x2 , y2

où (x1,y1) et (x2,y2) sont les points aux extrémités du segment.

Les segments de ligne sont affichés en utilisant la première syntaxe.

8.8.4. Boîtes
Les boîtes (rectangles) sont représentées par les paires de points des coins opposés de la boîte selon
une des syntaxes suivantes :

( ( x1 , y1 ) , ( x2 , y2 ) )
( x1 , y1 ) , ( x2 , y2 )
x1 , y1 , x2 , y2

où (x1,y1) et (x2,y2) sont les coins opposés du rectangle.

166
Types de données

Les rectangles sont affichés selon la deuxième syntaxe.

Les deux coins opposés peuvent être fournis en entrée, mais les valeurs seront réordonnées pour stocker
les coins en haut à droite et en bas à gauche, dans cet ordre.

8.8.5. Chemins
Les chemins ( type path ) sont représentés par des listes de points connectés. Ils peuvent être ouverts,
si le premier et le dernier point ne sont pas considérés comme connectés, ou fermés, si le premier et
le dernier point sont considérés comme connectés.

Les valeurs de type path sont saisies selon une des syntaxes suivantes :

[ ( x1 , y1 ) , ... , ( xn , yn ) ]
( ( x1 , y1 ) , ... , ( xn , yn ) )
( x1 , y1 ) , ... , ( xn , yn )
( x1 , y1 , ... , xn , yn )
x1 , y1 , ... , xn , yn

où les points sont les extrémités des segments de droite qui forment le chemin. Les crochets ([])
indiquent un chemin ouvert alors que les parenthèses (()) indiquent un chemin fermé. Quand les
parenthèses externes sont omises, comme dans les syntaxes trois à cinq, un chemin fermé est utilisé.

Les chemins sont affichés selon la première ou la seconde syntaxe appropriée.

8.8.6. Polygones
Les polygones (type polygon) sont représentés par des listes de points (les vertex du polygone). Ils
sont très similaires à des chemins fermés, mais ils sont stockés différemment et disposent de leurs
propres routines de manipulation.

Les valeurs de type polygon sont saisies selon une des syntaxes suivantes :

( ( x1 , y1 ) , ... , ( xn , yn ) )
( x1 , y1 ) , ... , ( xn , yn )
( x1 , y1 , ... , xn , yn )
x1 , y1 , ... , xn , yn

où les points sont les extrémités des segments de droite qui forment les limites du polygone.

Les polygones sont affichés selon la première syntaxe.

8.8.7. Cercles
Les cercles (type circle) sont représentés par un point central et un rayon. Les valeurs de type
circle sont saisies selon une des syntaxes suivantes :

< ( x , y ) , r >
( ( x , y ) , r )
( x , y ) , r
x , y , r

où (x,y) est le point central et r le rayon du cercle.

Les cercles sont affichés selon la première syntaxe.

8.9. Types adresses réseau


167
Types de données

PostgreSQL propose des types de données pour stocker des adresses IPv4, IPv6 et MAC. Ceux-ci sont
décrits dans le Tableau 8.21. Il est préférable d'utiliser ces types plutôt que des types texte standard
pour stocker les adresses réseau, car ils offrent un contrôle de syntaxe lors de la saisie et plusieurs
opérateurs et fonctions spécialisés (voir la Section 9.12).

Tableau 8.21. Types d'adresses réseau


Nom Taille de stockage Description
cidr 7 ou 19 octets réseaux IPv4 et IPv6
inet 7 ou 19 octets hôtes et réseaux IPv4 et IPv6
macaddr 6 octets adresses MAC
macaddr8 8 bytes adresses MAC (format EUI-64)

Lors du tri de données de types inet ou cidr, les adresses IPv4 apparaissent toujours avant les
adresses IPv6, y compris les adresses IPv4 encapsulées, comme ::10.2.3.4 ou ::ffff:10.4.3.2.

8.9.1. inet
Le type inet stocke une adresse d'hôte IPv4 ou IPv6 et, optionnellement, son sous-réseau, le tout
dans un seul champ. Le sous-réseau est représenté par le nombre de bits de l'adresse hôte constituant
l'adresse réseau (le « masque réseau »). Si le masque réseau est 32 et l'adresse de type IPv4, alors la
valeur n'indique pas un sous-réseau, juste un hôte. En IPv6, la longueur de l'adresse est de 128 bits,
si bien que 128 bits définissent une adresse réseau unique. Pour n'accepter que des adresses réseau, il
est préférable d'utiliser le type cidr plutôt que le type inet.

Le format de saisie pour ce type est adresse/y où adresse est une adresse IPv4 ou IPv6 et y
est le nombre de bits du masque réseau. Si y est omis, alors le masque vaut 32 pour IPv4 et 128 pour
IPv6, et la valeur représente un hôte unique. À l'affichage, la portion /y est supprimée si le masque
réseau indique un hôte unique.

8.9.2. cidr
Le type cidr stocke une définition de réseau IPv4 ou IPv6. La saisie et l'affichage suivent les
conventions Classless Internet Domain Routing. Le format de saisie d'un réseau est address/y où
address est le réseau représenté sous forme d'une adresse IPv4 ou IPv6 et y est le nombre de bits du
masque réseau. Si y est omis, il calculé en utilisant les règles de l'ancien système de classes d'adresses,
à ceci près qu'il est au moins assez grand pour inclure tous les octets saisis. Saisir une adresse réseau
avec des bits positionnés à droite du masque indiqué est une erreur.

Tableau 8.22 présente quelques exemples.

Tableau 8.22. Exemples de saisie de types cidr


Saisie cidr Affichage cidr abbrev(cidr)
192.168.100.128/25 192.168.100.128/25 192.168.100.128/25
192.168/24 192.168.0.0/24 192.168.0/24
192.168/25 192.168.0.0/25 192.168.0.0/25
192.168.1 192.168.1.0/24 192.168.1/24
192.168 192.168.0.0/24 192.168.0/24
128.1 128.1.0.0/16 128.1/16
128 128.0.0.0/16 128.0/16
128.1.2 128.1.2.0/24 128.1.2/24
10.1.2 10.1.2.0/24 10.1.2/24

168
Types de données

Saisie cidr Affichage cidr abbrev(cidr)


10.1 10.1.0.0/16 10.1/16
10 10.0.0.0/8 10/8
10.1.2.3/32 10.1.2.3/32 10.1.2.3/32
2001:4f8:3:ba::/64 2001:4f8:3:ba::/64 2001:4f8:3:ba::/64
2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128
2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128
2001:4f8:3:ba:2e0:81ff:fe22:d1f1
::ffff:1.2.3.0/120 ::ffff:1.2.3.0/120 ::ffff:1.2.3/120
::ffff:1.2.3.0/128 ::ffff:1.2.3.0/128 ::ffff:1.2.3.0/128

8.9.3. inet vs cidr


La différence principale entre les types de données inet et cidr réside dans le fait que inet accepte
des valeurs avec des bits non nuls à droite du masque de réseau, alors que cidr ne l'accepte pas. Par
exemple, 192.168.0.1/24 est valide pour inet, mais pas pour cidr.

Astuce
Les fonctions host, text et abbrev permettent de modifier le format d'affichage des
valeurs inet et cidr.

8.9.4. macaddr
Le type macaddr stocke des adresses MAC, connues par exemple pour les adresses de cartes réseau
Ethernet (mais les adresses MAC sont aussi utilisées dans d'autres cas). Les saisies sont acceptées
dans les formats suivants :

'08:00:2b:01:02:03'
'08-00-2b-01-02-03'
'08002b:010203'
'08002b-010203'
'0800-2b01-0203'
'08002b010203'

Ces exemples indiquent tous la même adresse. Les majuscules et les minuscules sont acceptées pour
les chiffres a à f. L'affichage se fait toujours selon le premier des formats ci-dessus.

Le standard IEEE 802-2001 spécifie la seconde forme affichée (avec les tirets) comme forme
canonique pour les adresses MAC, et la première forme (avec les :) comme utilisé avec la notation à
bits retournés, MSB en premier, ce qui donne l'équivalence 08-00-2b-01-02-03 = 01:00:D4:80:40:C0.
Cette convention est largement ignorée aujourd'hui et n'a de sens que pour des protocoles réseau
obsolètes (comme Token Ring). PostgreSQL ne tient pas compte des bits retournés ; tous les formats
acceptés utilisent l'ordre canonique LSB.

Les cinq derniers formats ne font partie d'aucun standard.

8.9.5. macaddr8
Le type macaddr8 stocke des adresses MAC au format EUI-64, connu par exemple pour les adresses
de cartes réseau Ethernet (mais les adresses MAC sont aussi utilisées dans d'autres cas). Ce type
accepte à la fois des adresses MAC d'une longueur de six et huit octets. Les adresses MAC fournies
dans un format de six octets seront stockées dans un format de huit octets avec les quatrième et
cinquième octets respectivement positionnés à FF et FE. Veuillez noter qu'IPv6 utilise un format

169
Types de données

modifié de EUI-64 où le septième bit devrait être positionné à un après la conversion depuis EUI-48.
La fonction macaddr8_set7bit est fournie pour réaliser ce changement. De manière générale,
n'importe quelle valeur en entrée constituée de paires de chiffres au format hexadécimal (dans les
limites d'un octet), systématiquement séparées ou non d'un de ces caractères ':', '-' ou '.' est
acceptée. Le nombre de chiffres hexadécimaux doit être 16 (huit octets) ou 12 (six octets). Les espaces
non significatifs présents avant ou après sont ignorés. Voici un ensemble d'exemples de formats
acceptés en entrée :

'08:00:2b:01:02:03:04:05'
'08-00-2b-01-02-03-04-05'
'08002b:0102030405'
'08002b-0102030405'
'0800.2b01.0203.0405'
'0800-2b01-0203-0405'
'08002b01:02030405'
'08002b0102030405'

Ces exemples spécifient tous la même adresse. Les majuscules et les minuscules sont acceptées pour
les caractères de a jusqu'à f. La sortie sera toujours au même format que le premier exemple. Les
six derniers formats en entrée qui sont mentionnés au-dessus ne font partie d'aucun standard. Pour
convertir une adresse MAC traditionnelle de 48 bits au format EUI-48 vers le format modifié EUI-64
pour pouvoir être incluse dans la partie hôte d'une adresse IPv6, utilisez macaddr8_set7bit
comme ceci :

SELECT macaddr8_set7bit('08:00:2b:01:02:03');

macaddr8_set7bit
-------------------------
0a:00:2b:ff:fe:01:02:03
(1 row)

8.10. Type chaîne de bits


Les chaînes de bits sont des chaînes de 0 et de 1. Elles peuvent être utilisées pour stocker ou visualiser
des masques de bits. Il y a deux types bits en SQL : bit(n) et bit varying(n), avec n un
entier positif.

Les données de type bit doivent avoir une longueur de n bits exactement. Essayer de lui affecter une
chaîne de bits plus longue ou plus courte déclenche une erreur. Les données de type bit varying
ont une longueur variable, d'au maximum n bits ; les chaînes plus longues sont rejetées. Écrire bit
sans longueur est équivalent à bit(1), alors que bit varying sans longueur indique une taille
illimitée.

Note
Lors du transtypage explicite (cast) d'une chaîne de bits en champ de type bit(n), la chaîne
obtenue est complétée avec des zéros ou bien tronquée pour obtenir une taille de n bits
exactement, sans que cela ne produise une erreur. De la même façon, si une chaîne de bits
est explicitement transtypée en un champ de type bit varying(n), elle est tronquée si
sa longueur dépasse n bits.

Voir la Section 4.1.2.5 pour plus d'information sur la syntaxe des constantes en chaîne de bits. Les
opérateurs logiques et les fonctions de manipulation de chaînes sont décrits dans la Section 9.6.

170
Types de données

Exemple 8.3. Utiliser les types de chaînes de bits


CREATE TABLE test (a BIT(3), b BIT VARYING(5));
INSERT INTO test VALUES (B'101', B'00');
INSERT INTO test VALUES (B'10', B'101');

ERROR: bit string length 2 does not match type bit(3)

INSERT INTO test VALUES (B'10'::bit(3), B'101');


SELECT * FROM test;

a | b
-----+-----
101 | 00
100 | 101

Une valeur pour une chaîne de bits nécessite un octet pour chaque groupe de huit bits, plus cinq ou
huit octets d'en-tête suivant la longueur de la chaîne (les valeurs longues peuvent être compressées ou
déplacées, comme expliqué dans Section 8.3 pour les chaînes de caractères).

8.11. Types de recherche plein texte


PostgreSQL fournit deux types de données conçus pour supporter la recherche plein texte qui est
l'activité de recherche via une collection de documents en langage naturel pour situer ceux qui
correspondent le mieux à une requête. Le type tsvector représente un document dans une forme
optimisée pour la recherche plein texte alors que le type tsquery représente de façon similaire une
requête. Chapitre 12 fournit une explication détaillée de cette capacité et Section 9.13 résume les
fonctions et opérateurs en relation.

8.11.1. tsvector
Une valeur tsvector est une liste triée de lexemes distincts, qui sont des mots qui ont été normalisés
pour fusionner différentes variantes du même mot apparaissant (voir Chapitre 12 pour plus de détails).
Trier et éliminer les duplicats se font automatiquement lors des entrées, comme indiqué dans cet
exemple :

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector;
tsvector
----------------------------------------------------
'a' 'and' 'ate' 'cat' 'fat' 'mat' 'on' 'rat' 'sat'

Pour représenter des lexèmes contenant des espaces blancs ou des signes de ponctuation, entourez-
les avec des guillemets simples :

SELECT $$the lexeme ' ' contains spaces$$::tsvector;


tsvector
-------------------------------------------
' ' 'contains' 'lexeme' 'spaces' 'the'

(Nous utilisons les valeurs littérales entre guillemets simples dans cet exemple et dans le prochain
pour éviter une confusion en ayant à doubler les guillemets à l'intérieur des valeurs littérales.) Les
guillemets imbriqués et les antislashs doivent être doublés :

171
Types de données

SELECT $$the lexeme 'Joe''s' contains a quote$$::tsvector;


tsvector
------------------------------------------------
'Joe''s' 'a' 'contains' 'lexeme' 'quote' 'the'

En option, les positions peuvent être attachées aux lexèmes :

SELECT 'a:1 fat:2 cat:3 sat:4 on:5 a:6 mat:7 and:8 ate:9 a:10
fat:11 rat:12'::tsvector;
tsvector
-------------------------------------------------------------------------------
'a':1,6,10 'and':8 'ate':9 'cat':3 'fat':2,11 'mat':7 'on':5
'rat':12 'sat':4

Une position indique normalement l'emplacement du mot source dans le document. Les informations
de position sont utilisables pour avoir un score de proximité. Les valeurs des positions peuvent aller
de 1 à 16383 ; les grands nombres sont limités silencieusement à 16383. Les positions dupliquées du
même lexème sont rejetées.

Les lexèmes qui ont des positions peuvent aussi avoir un label d'un certain poids. Les labels possibles
sont A, B, C ou D. D est la valeur par défaut et n'est du coup pas affiché en sortie :

SELECT 'a:1A fat:2B,4C cat:5D'::tsvector;


tsvector
----------------------------
'a':1A 'cat':5 'fat':2B,4C

Les poids sont typiquement utilisés pour refléter la structure du document en marquant les mots du
titre de façon différente des mots du corps. Les fonctions de score de la recherche plein texte peuvent
assigner des priorités différentes aux marqueurs de poids différents.

Il est important de comprendre que le type tsvector lui-même ne réalise aucune normalisation de
mots ; il suppose que les mots qui lui sont fournis sont normalisés correctement pour l'application.
Par exemple,

SELECT 'The Fat Rats'::tsvector;


tsvector
--------------------
'Fat' 'Rats' 'The'

Pour la plupart des applications de recherche en anglais, les mots ci-dessus seraient considérés
comme non normalisés, mais tsvector n'y prête pas attention. Le texte des documents bruts doit
habituellement passer via to_tsvector pour normaliser les mots de façon appropriée pour la
recherche :

SELECT to_tsvector('english', 'The Fat Rats');


to_tsvector
-----------------
'fat':2 'rat':3

De nouveau, voir Chapitre 12 pour plus de détails.

172
Types de données

8.11.2. tsquery
Une valeur tsquery enregistre les lexèmes qui doivent être recherchés, et peut les combiner en
utilisant les opérateurs booléens & (AND), | (OR) et ! (NOT), ainsi que l'opérateur de recherche de
phrase <-> (FOLLOWED BY). Il existe aussi une variante de l'opérateur FOLLOWED BY, <N>,
où N est une constante entière indiquant la distance maximale entre les deux lexèmes recherchés. <-
> est équivalent à <1>.

Les parenthèses peuvent être utilisées pour forcer le regroupement des opérateurs. En l'absence de
parenthèses, ! (NOT) est prioritaire, <-> (FOLLOWED BY) suit, et enfin & (AND) et | (OR) sont
les moins prioritaires.

Voici quelques exemples :

SELECT 'fat & rat'::tsquery;


tsquery
---------------
'fat' & 'rat'

SELECT 'fat & (rat | cat)'::tsquery;


tsquery
---------------------------
'fat' & ( 'rat' | 'cat' )

SELECT 'fat & rat & ! cat'::tsquery;


tsquery
------------------------
'fat' & 'rat' & !'cat'

En option, les lexèmes dans une tsquery peuvent être labelisés avec une lettre de poids ou plus, ce
qui les restreint à une correspondance avec les seuls lexèmes tsvector pour un de ces poids :

SELECT 'fat:ab & cat'::tsquery;


tsquery
------------------
'fat':AB & 'cat'

Par ailleurs, les lexèmes d'une tsquery peuvent être marqués avec * pour spécifier une
correspondance de préfixe :

SELECT 'super:*'::tsquery;
tsquery
-----------
'super':*

Cette requête fera ressortir tout mot dans un tsvector qui commence par « super ».

Les règles de guillemets pour les lexèmes sont identiques à celles décrites ci-dessus pour les lexèmes
de tsvector ; et, comme avec tsvector, toute normalisation requise des mots doit se faire avant
de les placer dans le type tsquery. La fonction to_tsquery est convenable pour réaliser une telle
normalisation :

173
Types de données

SELECT to_tsquery('Fat:ab & Cats');


to_tsquery
------------------
'fat':AB & 'cat'

Notez que to_tsquery traitera les préfixes de la même façon que les autres mots, ce qui signifie
que cette comparaison renvoie true :

SELECT to_tsvector( 'postgraduate' ) @@ to_tsquery( 'postgres:*' );


?column?
----------
t

parce que postgres devient postgr :

SELECT to_tsvector( 'postgraduate' ), to_tsquery( 'postgres:*' );


to_tsvector | to_tsquery
---------------+------------
'postgradu':1 | 'postgr':*

qui correspondra à la forme native de postgraduate.

8.12. Type UUID


Le type de données uuid stocke des identifiants universels uniques (UUID, acronyme de Universally
Unique Identifiers) décrits dans les standards RFC 4122, ISO/IEC 9834-8:2005, et d'autres encore.
(Certains systèmes font référence à ce type de données en tant qu'identifiant unique global (ou GUID
).) Un identifiant de ce type est une quantité sur 128 bits générée par un algorithme adéquat qui a
peu de chances d'être reproduit par quelqu'un d'autre utilisant le même algorithme. Du coup, pour les
systèmes distribués, ces identifiants fournissent une meilleure garantie d'unicité que ce que pourrait
fournir une séquence, dont la valeur est unique seulement au sein d'une base de données.

Un UUID est écrit comme une séquence de chiffres hexadécimaux en minuscule, répartis en différents
groupes, séparés par un tiret. Plus précisément, il s'agit d'un groupe de huit chiffres suivis de trois
groupes de quatre chiffres terminés par un groupe de douze chiffres, ce qui fait un total de 32 chiffres
représentant les 128 bits. Voici un exemple d'UUID dans sa forme standard :

a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11

PostgreSQL accepte aussi d'autres formes en entrée : utilisation des majuscules, de crochets englobant
le nombre, suppression d'une partie ou de tous les tirets, ajout d'un tiret après n'importe quel groupe
de quatre chiffres. Voici quelques exemples :

A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}

L'affichage est toujours dans la forme standard.

174
Types de données

Pour générer des UUID, le module uuid-ossp fournit des fonctions qui implémentent les algorithmes
standards. Le module pgcrypto fournit également une fonction de génération d'UUID aléatoires. Sinon,
les UUID peuvent être générés par des applications clientes ou par d'autres bibliothèques appelées par
une fonction serveur.

8.13. Type XML


Le type de données xml est utilisé pour stocker des données au format XML. Son avantage sur un
champ de type text est qu'il vérifie que les valeurs sont bien formées. De plus, il existe de nombreuses
fonctions pour réaliser des opérations de vérification à partir de ce type ; voir la Section 9.14.
L'utilisation de ce type de données requiert que l'étape de compilation ait utilisé l'option --with-
libxml.

Le type xml peut stocker des « documents » bien formés, suivant la définition du standard XML, ainsi
que des fragments de contenu (« content »), en référence au « nœud de document »1 plus permissif des
modèle de données XQuery et XPath. Cela signifie que les fragments de contenu peuvent avoir plus
d'un élément racine ou nœud caractère. L'expression valeurxml IS DOCUMENT permet d'évaluer
si une valeur xml particulière est un document complet ou seulement un fragment de contenu.

Les limites et notes de compatibilité pour le type de données xml sont disponibles dans Section D.3.

8.13.1. Créer des valeurs XML


Pour produire une valeur de type xml à partir d'une donnée de type caractère, utilisez la fonction
xmlparse :

XMLPARSE ( { DOCUMENT | CONTENT } valeur)

Quelques exemples :

XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</


title><chapter>...</chapter></book>')
XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')

Bien que cela soit la seule façon de convertir des chaînes de caractères en valeurs XML d'après le
standard XML, voici des syntaxes spécifiques à PostgreSQL :

xml '<foo>bar</foo>'
'<foo>bar</foo>'::xml

Le type xml ne valide pas les valeurs en entrée par rapport à une déclaration de type de document
(DTD), même quand la valeur en entrée indique une DTD. Il n'existe pas encore de support pour la
validation avec d'autres langages de schéma XML, comme XML Schema.

L'opération inverse, produisant une chaîne de caractères à partir d'une valeur au type xml, utilise la
fonction xmlserialize:

XMLSERIALIZE ( { DOCUMENT | CONTENT } value AS type )

type peut être character, character varying ou text (ou un alias de ces derniers). Encore
une fois, d'après le standard SQL, c'est le seul moyen de convertir le type xml vers les types caractère,
mais PostgreSQL autorise aussi la conversion simple de la valeur.
1
https://www.w3.org/TR/2010/REC-xpath-datamodel-20101214/#DocumentNode

175
Types de données

Lorsque les valeurs des chaînes de caractères sont converties vers ou à partir du type xml sans
passer par XMLPARSE ou XMLSERIALIZE, respectivement, le choix de DOCUMENT ou de CONTENT
est déterminé par un paramètre de configuration niveau session, « XML OPTION » , qui peut être
configuré par la commande habituelle :

SET XML OPTION { DOCUMENT | CONTENT };

ou la syntaxe PostgreSQL :

SET xmloption TO { DOCUMENT | CONTENT };

La valeur par défaut est CONTENT, donc toutes les formes de données XML sont autorisées.

8.13.2. Gestion de l'encodage


Une grande attention doit prévaloir lors de la gestion de plusieurs encodages sur le client, le serveur
ou dans les données XML qui passent entre eux. Lors de l'utilisation du mode texte pour passer les
requêtes au serveur et pour renvoyer les résultats au client (qui se trouve dans le mode normal),
PostgreSQL convertit toutes les données de type caractère passées entre le client et le serveur et
vice-versa suivant l'encodage spécifique de la destination finale ; voir la Section 23.3. Cela inclut les
représentations textuelles des valeurs XML, comme dans les exemples ci-dessus, ce qui signifie que
les déclarations d'encodage contenues dans les données XML pourraient devenir invalides lorsque les
données sont converties vers un autre encodage lors du transfert entre le client et le serveur, alors que la
déclaration de l'encodage n'est pas modifiée. Pour s'en sortir, une déclaration d'encodage contenue dans
une chaîne de caractères présentée en entrée du type xml est ignorée, et le contenu est toujours supposé
être de l'encodage du serveur. En conséquence, pour un traitement correct, ces chaînes de caractères
de données XML doivent être envoyées du client dans le bon encodage. C'est de la responsabilité du
client de soit convertir le document avec le bon encodage client avant de l'envoyer au serveur, soit
d'ajuster l'encodage client de façon appropriée. En sortie, les valeurs du type xml n'auront pas une
déclaration d'encodage et les clients devront supposer que les données sont dans l'encodage du client.

Lors de l'utilisation du mode binaire pour le passage des paramètres de la requête au serveur et des
résultats au client, aucune conversion de l'encodage n'est réalisée, donc la situation est différente. Dans
ce cas, une déclaration d'encodage dans les données XML sera observée et, si elle est absente, les
données seront supposées être en UTF-8 (comme requis par le standard XML ; notez que PostgreSQL
ne supporte pas du tout UTF-16). En sortie, les données auront une déclaration d'encodage spécifiant
l'encodage client, sauf si l'encodage client est UTF-8, auquel cas elle sera omise.

Le traitement des données XML avec PostgreSQL sera moins complexe et plus efficace si l'encodage
des données, l'encodage client et l'encodage serveur sont identiques. Comme les données XML sont
traitées en interne en UTF-8, les traitements seront plus efficaces si l'encodage serveur est aussi en
UTF-8.

Attention
Certaines fonctions relatives à XML pourraient ne pas fonctionner du tout sur des données
non ASCII quand l'encodage du serveur n'est pas UTF-8. C'est un problème connu pour
xmltable() et xpath() en particulier.

8.13.3. Accéder aux valeurs XML


Le type de données xml est inhabituel dans le sens où il ne dispose pas d'opérateurs de comparaison.
Ceci est dû au fait qu'il n'existe pas d'algorithme de comparaison bien défini et utile pour des données

176
Types de données

XML. Une conséquence de ceci est que vous ne pouvez pas récupérer des lignes en comparant une
colonne xml avec une valeur de recherche. Les valeurs XML doivent du coup être typiquement
accompagnées par un champ clé séparé comme un identifiant. Une autre solution pour la comparaison
de valeurs XML est de les convertir en des chaînes de caractères, mais notez que la comparaison de
chaînes n'a que peu à voir avec une méthode de comparaison XML utile.

Comme il n'y a pas d'opérateurs de comparaison pour le type de données xml, il n'est pas possible
de créer un index directement sur une colonne de ce type. Si une recherche rapide est souhaitée dans
des données XML, il est toujours possible de convertir l'expression en une chaîne de caractères et
d'indexer cette conversion. Il est aussi possible d'indexer une expression XPath. La vraie requête devra
bien sûr être ajustée à une recherche sur l'expression indexée.

La fonctionnalité de recherche plein texte peut aussi être utilisée pour accélérer les recherches dans
des données XML. Le support du prétraitement nécessaire n'est cependant pas disponible dans la
distribution PostgreSQL.

8.14. Types JSON


Les types de données JSON sont faits pour stocker des données JSON (JavaScript Object Notation),
comme spécifié dans la RFC 71592. De telles données peuvent également être stockées comme text
, mais les types de données JSON ont l'avantage d'assurer que chaque valeur stockée est valide d'après
les règles JSON. Il y a également des fonctions et opérateurs spécifiques à JSON associés disponibles
pour les données stockées dans ces types de données. Voir Section 9.15.

Il y a deux types de données JSON : json et jsonb. Ils acceptent quasiment des ensembles de valeurs
identiques en entrée. La différence majeure réside dans l'efficacité. Le type de données json stocke
une copie exacte du texte en entrée, que chaque fonction doit analyser à chaque exécution, alors que le
type de données jsonb est stocké dans un format binaire décomposé qui rend l'insertion légèrement
plus lente du fait du surcoût de la conversion, mais est significativement plus rapide pour traiter les
données, puisqu'aucune analyse n'est nécessaire. jsonb gère également l'indexation, ce qui peut être
un avantage significatif.

Puisque le type json stocke une copie exacte du texte en entrée, il conservera les espaces
sémantiquement non significatifs entre les jetons, ainsi que l'ordre des clés au sein de l'objet JSON. De
plus, si un objet JSON contient dans sa valeur la même clé plus d'une fois, toutes les paires clé/valeur
sont conservées (les fonctions de traitement considèrent la dernière valeur comme celle significative).
À l'inverse, jsonb ne conserve ni les espaces non significatifs, ni l'ordre des clés d'objet, ni ne
conserve les clés d'objet dupliquées. Si des clés dupliquées sont présentées en entrée, seule la dernière
valeur est conservée.

En général, la plupart des applications devraient préférer stocker les données JSON avec jsonb, à
moins qu'il y ait des besoins spécifiques, comme la supposition légitime de l'ordre des clés d'objet.

PostgreSQL n'autorise qu'un seul encodage de caractères par base de données. Il n'est donc pas possible
pour les types JSON de se conformer de manière rigoureuse à la spécification JSON, à moins que
l'encodage de la base de données soit UTF8. Tenter d'inclure directement des caractères qui ne peuvent
pas être représentés dans l'encodage de la base de données échouera ; inversement, des caractères qui
peuvent être représentés dans l'encodage de la base de données, mais pas en UTF8, seront autorisés.

La RFC 7159 autorise les chaînes JSON à contenir des séquences Unicode échappées, indiquées avec
\uXXXX. Dans la fonction d'entrée pour le type json, les échappements Unicode sont autorisés quel
que soit l'encodage de la base de données, et sont vérifiés uniquement pour l'exactitude de la syntaxe
(qui est quatre chiffres hexadécimaux précédés d'un \u). Toutefois, la fonction d'entrée pour jsonb
est plus stricte : elle interdit les échappements Unicode pour les caractères autres que ASCII (ceux au-
delà de U+007F) à moins que l'encodage de la base de données soit UTF8. Le type jsonb rejette
aussi \u0000 (parce qu'il ne peut pas être représenté avec le type text de PostgreSQL), et il insiste
pour que chaque utilisation de paires de substitution Unicode désignant des caractères en dehors du
2
https://tools.ietf.org/html/rfc7159

177
Types de données

Unicode Basic Multilingual Plane soit correcte. Les échappements Unicode valides sont convertis en
leur caractère ASCII ou UTF8 équivalent pour du stockage ; ceci inclut les « folding surrogate pairs »
sur un seul caractère.

Note
De nombreuses fonctions de traitement JSON décrites dans Section 9.15 convertiront les
échappements Unicode vers des caractères standards, et généreront donc le même type
d'erreurs décrit juste avant si leur entrée est de type json et non jsonb. Le fait que la fonction
d'entrée json ne fasse pas ces vérifications peut être considéré comme un artefact historique,
bien qu'elle n'autorise pas un simple stockage (sans traitement) d'échappements Unicode JSON
dans une base de données en encodage non UTF8. En général, il est préférable d'éviter de
mélanger des échappements Unicode en JSON avec une base de données en encodage non
UTF8 si possible.

Lors de la conversion de données texte JSON vers jsonb, les types primitifs décrits par la RFC 7159
sont transcrits efficacement vers des types PostgreSQL natifs, comme indiqué dans Tableau 8.23.
Par conséquent, il y a quelques contraintes additionnelles mineures sur ce qui constitue des données
jsonb valides qui ne s'appliquent ni au type json, ni à JSON en définitive, correspondant aux limites
de ce qui peut être représenté par le type de données sous-jacent. Spécifiquement, jsonb rejettera
les nombres qui sont en dehors de la portée du type de données numeric de PostgreSQL, alors que
json les acceptera. De telles restrictions définies par l'implémentation sont permises par la RFC 7159.
Cependant, en pratique, de tels problèmes ont beaucoup plus de chances de se produire dans d'autres
implémentations, puisqu'il est habituel de représenter les types primitifs number JSON comme des
nombres flottants à double précision (IEEE 754 double precision floating point), ce que la RFC 7159
anticipe explicitement et autorise. Lorsque JSON est utilisé comme format d'échange avec de tels
systèmes, le risque de perte de précision pour les valeurs numériques comparées aux données stockées
à l'origine par PostgreSQL devrait être considéré.

À l'inverse, comme indiqué dans le tableau, il y a quelques restrictions mineures sur le format d'entrée
de types primitifs JSON qui ne s'appliquent pas aux types PostgreSQL correspondants.

Tableau 8.23. Types primitifs JSON et types PostgreSQL correspondants


Type primitif JSON Type PostgreSQL Notes
string text \u0000 est interdit, tout
comme les échappements
Unicode non-ASCII si
l'encodage de la base de données
n'est pas UTF8
number numeric Les valeurs NaN et infinity
sont interdites
boolean boolean Seules les versions en minuscule
de true et false sont
acceptées
null (none) NULL dans SQL est un concept
différent

8.14.1. Syntaxe d'entrée et de sortie JSON


La syntaxe d'entrée/sortie pour les types de données JSON est identique à celle spécifiée dans la RFC
7159.

Les exemples suivants sont tous des expressions json (ou jsonb) valides :

178
Types de données

-- Simple valeur scalaire/primitive


-- Les valeurs primitives peuvent être des nombres, chaînes entre
guillemets, true, false ou null
SELECT '5'::json;

-- Tableau de zéro ou plus éléments (les éléments doivent être du


même type)
SELECT '[1, 2, "foo", null]'::json;

-- Objets contenant des paires de clé et valeurs


-- À noter que les clés d'objets doivent toujours être des chaînes
entre guillemets
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;

-- Tableaux et objets peuvent être imbriqués arbitrairement


SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;

Comme dit précédemment, quand une valeur JSON est renseignée puis affichée sans traitement
additionnel, json renvoie le même texte qui était fourni en entrée, alors que jsonb ne préserve pas
les détails sémantiquement non significatifs comme les espaces. Par exemple, il faut noter la différence
ici :

SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;


json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)

SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;


jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)

un détail sémantiquement non significatif qu'il faut souligner est qu'avec jsonb, les nombres seront
affichés en fonction du type numeric sous-jacent. En pratique, cela signifie que les nombres
renseignés avec la notation E seront affichés sans. Par exemple :

SELECT '{"reading": 1.230e-5}'::json, '{"reading":


1.230e-5}'::jsonb;
json | jsonb
-----------------------+-------------------------
{"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)

Toutefois, jsonb préservera les zéros en fin de partie fractionnaire, comme on peut le voir dans cet
exemple, même si ceux-ci ne sont pas sémantiquement significatifs, pour des besoins tels que des
tests d'égalité.

8.14.2. Concevoir des documents JSON efficacement


Représenter des données en JSON peut être considérablement plus flexible que le modèle de données
relationnel traditionnel, qui est contraignant dans des environnements où les exigences sont souples.

179
Types de données

Il est tout à fait possible que ces deux approches puissent coexister, et qu'elles soient complémentaires
au sein de la même application. Toutefois, même pour les applications où on désire le maximum de
flexibilité, il est toujours recommandé que les documents JSON aient une structure quelque peu fixée.
La structure est typiquement non vérifiée (bien que vérifier des règles métier de manière déclarative
soit possible), mais le fait d'avoir une structure prévisible rend plus facile l'écriture de requêtes qui
résument utilement un ensemble de « documents » (datums) dans une table.

Les données JSON sont sujettes aux mêmes considérations de contrôle de concurrence que pour
n'importe quel autre type de données quand elles sont stockées en table. Même si stocker de gros
documents est prévisible, il faut garder à l'esprit que chaque mise à jour acquiert un verrou de niveau
ligne sur toute la ligne. Il faut envisager de limiter les documents JSON à une taille gérable pour
réduire les contentions sur verrou lors des transactions en mise à jour. Idéalement, les documents JSON
devraient chacun représenter une donnée atomique, que les règles métiers imposent de ne pas pouvoir
subdiviser en données plus petites qui pourraient être modifiées séparément.

8.14.3. Existence et inclusion jsonb


Tester l'inclusion est une capacité importante de jsonb. Il n'y a pas d'ensemble de fonctionnalités
parallèles pour le type json. L'inclusion teste si un des documents jsonb est contenu dans un autre.
Ces exemples renvoient vrai, sauf note explicite :

-- Simple valeur scalaire/primitive qui contient une seule valeur


identique :
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;

-- Le tableau de droite est contenu dans celui de gauche :


SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;

-- L'ordre des éléments d'un tableau n'est pas significatif, donc


ceci est tout
-- aussi vrai :
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;

-- Les éléments dupliqués d'un tableau n'ont pas plus


d'importance :
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;

-- L'objet avec une seule paire à droite est contenu


-- dans l'objet sur le côté gauche :
SELECT '{"product": "PostgreSQL", "version": 9.4,
"jsonb":true}'::jsonb @> '{"version":9.4}'::jsonb;

-- Le tableau du côté droit n'est <emphasis>pas</emphasis>


considéré comme contenu
-- dans le tableau du côté gauche, même si un tableau similaire est
imbriqué dedans :
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- renvoie faux

-- Mais avec une couche d'imbrication, il est contenu :


SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;

-- De la même manière, l'inclusion n'est pas valable ici :


SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb;
-- renvoie faux

-- Une clé du niveau racine et un objet vide sont contenus :


SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;

180
Types de données

Le principe général est que l'objet inclus doit correspondre à l'objet devant le contenir à la fois pour la
structure et pour les données, peut-être après la suppression d'éléments de tableau ou d'objets paires
clé/valeur ne correspondant pas à l'objet contenant. Mais rappelez-vous que l'ordre des éléments dans
un tableau n'est pas significatif lors d'une recherche de contenance, et que les éléments dupliqués d'un
tableau ne sont réellement considérés qu'une seule fois.

Comme exception qui confirme la règle que les structures doivent correspondre, un tableau peut inclure
une valeur primitive :

-- Ce tableau inclut la valeur primitive chaîne :


SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb;

-- Cette exception n'est pas réciproque, la non-inclusion est


rapportée ici :
SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- renvoie faux

jsonb a également un opérateur d'existence, qui est une variation sur le thème de l'inclusion : il teste
si une chaîne (sous forme de valeur text) apparaît comme une clé d'objet ou un élément de tableau
au niveau supérieur de la valeur jsonb. Ces exemples renvoient vrai; sauf note explicite :

-- La chaîne existe comme un élément de tableau :


SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';

-- La chaîne existe comme une clé d'objet :


SELECT '{"foo": "bar"}'::jsonb ? 'foo';

-- Les valeurs d'objets ne sont pas examinées :


SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- renvoie faux

-- Comme pour l'inclusion, l'existence doit correspondre au niveau


supérieur :
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- renvoie faux

-- Une chaîne est examinée pour l'existence si elle correspond à


une primitive chaîne JSON :
SELECT '"foo"'::jsonb ? 'foo';

Les objets JSON sont plus adaptés que les tableaux pour tester l'inclusion ou l'existence quand il y a de
nombreux éléments ou clés impliqués, car contrairement aux tableaux, ils sont optimisés de manière
interne pour la recherche et n'ont pas besoin d'être parcourus linéairement.

Astuce
Comme les documents JSON sont imbriqués, une requête appropriée peut ignorer une sélection
explicite de sous-objets. Par exemple, supposons que nous ayons une colonne doc contenant
des objets au plus haut niveau, avec la plupart des objets contenant les champs tags qui
contiennent eux-mêmes des tableaux de sous-objets. Cette requête trouve des entrées dans
lesquelles les sous-objets contiennent à la fois "term":"paris" et "term":"food",
tout en ignorant ces clés en dehors du tableau tags :

SELECT doc->'site_name' FROM websites


WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';

181
Types de données

Cela pourrait s'accomplir aussi ainsi :

SELECT doc->'site_name' FROM websites


WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';

mais cette approche est moins flexible, et souvent bien moins efficace.

Mais l'opérateur JSON d'existence n'est pas imbriqué : il cherchera seulement pour la clé ou
l'élément de tableau spécifié à la racine de la valeur JSON.

Les différents opérateurs d'inclusion d'existence, avec tous les autres opérateurs et fonctions JSON,
sont documentés dans Section 9.15.

8.14.4. Indexation jsonb


Les index GIN peuvent être utilisés pour chercher efficacement des clés ou paires clé/valeur se trouvant
parmi un grand nombre de documents (datums) jsonb. Deux « classes d'opérateurs » GIN sont
fournies, offrant différents compromis entre performances et flexibilité.

La classe d'opérateur GIN par défaut pour jsonb supporte les requêtes avec des opérateurs de haut
niveau clé-existe ?, ?& et des opérateurs ?| et l'opérateur chemin/valeur-existe @>. (Pour des détails
sur la sémantique que ces opérateurs implémentent, voir Tableau 9.44.) Un exemple de création d'index
avec cette classe d'opérateurs est :

CREATE INDEX idxgin ON api USING GIN (jdoc);

La classe d'opérateurs GIN qui n'est pas par défaut jsonb_path_ops supporte l'indexation de
l'opérateur @> seulement. Un exemple de création d'index avec cette classe d'opérateurs est :

CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);

En étudiant l'exemple d'une table qui stocke des documents JSON récupérés par un service web tiers,
avec une définition de schéma documentée, un document typique serait :

{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}

182
Types de données

Ces documents sont stockés dans une table nommée api, dans une colonne de type jsonb nommée
jdoc. Si un index GIN est créé sur cette colonne, des requêtes semblables à l'exemple suivant peuvent
utiliser cet index :

-- Trouver les documents dans lesquels la clé "company" a pour


valeur "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @>
'{"company": "Magnafone"}';

Toutefois, cet index ne pourrait pas être utilisé pour des requêtes comme dans l'exemple suivant, car
bien que l'opérateur ? soit indexable, il n'est pas appliqué directement sur la colonne indexée jdoc :

-- Trouver les documents dans lesquels la clé "tags" contient une


clé ou un élément tableau "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ?
'qui';

Toutefois, avec l'utilisation appropriée d'index sur expression, la requête ci-dessus peut utiliser un
index. Si le requêtage d'éléments particuliers de la clé "tags" est fréquent, définir un index comme
ceci pourrait être particulièrement bénéfique :

-- À noter que l'opérateur "jsonb -> text" ne peut être appelé que
sur un
-- objet JSON, donc la conséquence de créer cet index est que le
premier niveau de
-- chaque valeur "jdoc" doit être un objet. Ceci est vérifié lors
de chaque insertion.
CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));

Dorénavant, la clause WHERE jdoc -> 'tags' ? 'qui' sera reconnue comme une application
de l'opérateur indexable ? pour l'expression indexée jdoc -> 'tags'. (Plus d'informations sur
les index sur expression peuvent être trouvées dans Section 11.7.)

Une autre approche pour le requêtage et l'exploitation de l'inclusion, par exemple :

-- Trouver les documents dans lesquels la clé "tags" inclut


l'élément tableau "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags":
["qui"]}';

Un simple index GIN sur la colonne jdoc peut répondre à cette requête. Mais il faut noter qu'un tel
index stockera des copies de chaque clé et chaque valeur de la colonne jdoc, alors que l'index sur
expression de l'exemple précédent ne stockera que les données trouvées pour la clé tags. Alors que
l'approche d'index simple est bien plus souple (puisqu'elle supporte les requêtes sur n'importe quelle
clé), les index sur des expressions ciblées ont bien plus de chances d'être plus petits et plus rapides
pour la recherche qu'un simple index.

Bien que la classe d'opérateur jsonb_path_ops ne supporte que les requêtes avec l'opérateur
@>, elle a des avantages de performances notables par rapport à la classe d'opérateur par
défaut jsonb_ops. Un index jsonb_path_ops est généralement bien plus petit qu'un index
jsonb_ops pour les mêmes données, et la spécificité de la recherche est meilleure, particulièrement
quand les requêtes contiennent des clés qui apparaissent fréquemment dans les données. Par

183
Types de données

conséquent, les opérations de recherche sont généralement plus performantes qu'avec la classe
d'opérateur par défaut.

La différence technique entre des index GIN jsonb_ops et jsonb_path_ops est que le premier
crée des éléments d'index indépendants pour chaque clé et valeur dans les données, alors que le second
crée des éléments d'index uniquement pour chaque valeur dans les données. 3 Fondamentalement,
chaque élément d'index jsonb_path_ops est un hachage de la valeur et de la ou des clés y menant ;
par exemple pour indexer {"foo": {"bar": "baz"}}, un seul élément dans l'index sera créé,
incorporant les trois foo, bar et baz dans une valeur hachée. Ainsi, une requête d'inclusion cherchant
cette structure résulterait en une recherche d'index extrêmement spécifique, mais il n'y a pas d'autre
moyen de savoir si foo apparaît en tant que clé. D'un autre côté, un index jsonb_ops créerait trois
éléments d'index représentant foo, bar et baz séparément ; ainsi, pour faire la requête d'inclusion,
il faudrait rechercher les lignes contenant chacun des trois éléments. Bien que les index GIN puissent
effectuer de telles recherches et de manière tout à fait efficace, cela sera toujours moins spécifique et
plus lent que la recherche équivalente jsonb_path_ops, surtout s'il y a un très grand nombre de
lignes contenant n'importe lequel des trois éléments d'index.

Un désavantage de l'approche jsonb_path_ops est qu'elle ne produit d'entrées d'index que pour
les structures JSON ne contenant aucune valeur, comme {"a": {}}. Si une recherche pour des
documents contenant une telle structure est demandée, elle nécessitera un parcours de la totalité de
l'index, ce qui peut être assez long. jsonb_path_ops est donc mal adapté pour des applications
qui effectuent souvent de telles recherches.

jsonb supporte également les index btree et hash. Ceux-ci ne sont généralement utiles que s'il
est important de vérifier l'égalité de documents JSON entiers. Le tri btree pour des données jsonb
est rarement d'un grand intérêt, mais afin d'être exhaustif, il est :

Objet > Tableau > Booléen > Nombre > Chaîne > Null

Objet avec n paires > objet avec n - 1 paires

Tableau avec n éléments > tableau avec n - 1 éléments

Les objets avec le même nombre de paires sont comparés dans cet ordre :

clé-1, valeur-1, clé-2 ...

À noter que les clés d'objet sont comparées dans leur ordre de stockage ; en particulier, puisque les
clés les plus courtes sont stockées avant les clés les plus longues, cela peut amener à des résultats
contre-intuitifs, tels que :

{ "aa": 1, "c": 1} > {"b": 1, "d": 1}

De la même manière, les tableaux avec le même nombre d'éléments sont comparés dans l'ordre :

élément-1, élément-2 ...

Les valeurs JSON primitives sont comparées en utilisant les mêmes règles de comparaison que pour
les types de données PostgreSQL sous-jacents. Les chaînes sont comparées en utilisant la collation
par défaut de la base de données.
3
Dans ce contexte, le terme « valeur » inclut les éléments de tableau, bien que la terminologie JSON considère parfois que les éléments de
tableaux soient distincts des valeurs dans les objets.

184
Types de données

8.14.5. Transformations
Des extensions supplémentaires sont disponibles pour implémenter des transformations pour le type
jsonb pour différents langages de procédure stockée.

Les extensions pour PL/Perl sont appelées jsonb_plperl et jsonb_plperlu. Si vous les
utilisez, les valeurs jsonb sont transformées en tableaux, hachages et scalaires Perl, suivant le cas.

Les extensions pour PL/Python sont appelées jsonb_plpythonu, jsonb_plpython2u et


jsonb_plpython3u (voir Section 46.1 pour la convention de nommage PL/Python). Si vous les
utilisez, les valeurs jsonb sont transformées en dictionnaires, listes et scalaires Python, suivant le cas.

8.15. Tableaux
PostgreSQL permet de définir des colonnes de table comme des tableaux multidimensionnels de
longueur variable. Il est possible de créer des tableaux de n'importe quel type utilisateur : de base,
énuméré, composé, intervalle, domaine.

8.15.1. Déclaration des types tableaux


La création de la table suivante permet d'illustrer l'utilisation des types tableaux :

CREATE TABLE sal_emp (


nom text,
paye_par_semaine integer[],
planning text[][]
);

Comme indiqué ci-dessus, un type de données tableau est nommé en ajoutant des crochets ([])
au type de données des éléments du tableau. La commande ci-dessus crée une table nommée
sal_emp avec une colonne de type text (nom), un tableau à une dimension de type integer
(paye_par_semaine), représentant le salaire d'un employé par semaine et un tableau à deux
dimensions de type text (planning), représentant le planning hebdomadaire de l'employé.

La syntaxe de CREATE TABLE permet de préciser la taille exacte des tableaux, par exemple :

CREATE TABLE tictactoe (


carres integer[3][3]
);

Néanmoins, l'implantation actuelle ignore toute limite fournie pour la taille du tableau, c'est-à-dire que
le comportement est identique à celui des tableaux dont la longueur n'est pas précisée.

De plus, l'implantation actuelle n'oblige pas non plus à déclarer le nombre de dimensions. Les tableaux
d'un type d'élément particulier sont tous considérés comme étant du même type, quels que soient leur
taille ou le nombre de dimensions. Déclarer la taille du tableau ou le nombre de dimensions dans
CREATE TABLE n'a qu'un but documentaire. Le comportement de l'application n'en est pas affecté.

Une autre syntaxe, conforme au standard SQL via l'utilisation du mot-clé ARRAY, peut être employée
pour les tableaux à une dimension. paye_par_semaine peut être défini ainsi :

paye_par_semaine integer ARRAY[4],

ou si aucune taille du tableau n'est spécifiée :

paye_par_semaine integer ARRAY,

185
Types de données

Néanmoins, comme indiqué précédemment, PostgreSQL n'impose aucune restriction sur la taille dans
tous les cas.

8.15.2. Saisie de valeurs de type tableau


Pour écrire une valeur de type tableau comme une constante littérale, on encadre les valeurs des
éléments par des accolades et on les sépare par des virgules (ce n'est pas différent de la syntaxe C
utilisée pour initialiser les structures). Des guillemets doubles peuvent être positionnés autour des
valeurs des éléments. C'est d'ailleurs obligatoire si elles contiennent des virgules ou des accolades
(plus de détails ci-dessous). Le format général d'une constante de type tableau est donc le suivant :

'{ val1 delim val2 delim ... }'

où delim est le caractère de délimitation pour ce type, tel qu'il est enregistré dans son entrée
pg_type. Parmi les types de données standards fournis par la distribution PostgreSQL, tous utilisent
une virgule (,), sauf pour le type box qui utilise un point-virgule (;). Chaque val est soit une
constante du type des éléments du tableau soit un sous-tableau.

Exemple de constante tableau :

'{{1,2,3},{4,5,6},{7,8,9}}'

Cette constante a deux dimensions, un tableau 3 par 3 consistant en trois sous-tableaux d'entiers.

Pour initialiser un élément d'un tableau à NULL, on écrit NULL pour la valeur de cet élément. (Toute
variante majuscule et/ou minuscule de NULL est acceptée.) Si « NULL » doit être utilisé comme valeur
de chaîne, on place des guillemets doubles autour.

Ces types de constantes tableau sont en fait un cas particulier des constantes de type générique abordées
dans la Section 4.1.2.7. La constante est traitée initialement comme une chaîne et passée à la routine
de conversion d'entrées de tableau. Une spécification explicite du type peut être nécessaire.

Quelques instructions INSERT :

INSERT INTO sal_emp


VALUES ('Bill',
'{10000, 10000, 10000, 10000}',
'{{"rendez-vous", "repas"}, {"entrainement",
"présentation"}}');

INSERT INTO sal_emp


VALUES ('Carol',
'{20000, 25000, 25000, 25000}',
'{{"petit-déjeuner", "consultation"}, {"rendez-vous",
"repas"}}');

Le résultat des deux insertions précédentes ressemble à :

SELECT * FROM sal_emp;


nom | paye_par_semaine | planning
-------+---------------------------+--------------------
Bill | {10000,10000,10000,10000} | {{rendez-vous,repas},
{entrainement,présentation}}
Carol | {20000,25000,25000,25000} | {{petit-
déjeuner,consultation},{rendez-vous,repas}}
(2 rows)

Les tableaux multidimensionnels doivent avoir des échelles correspondantes pour chaque dimension.
Une différence cause la levée d'une erreur. Par exemple :

186
Types de données

INSERT INTO sal_emp


VALUES ('Bill',
'{10000, 10000, 10000, 10000}',
'{{"rendez-vous", "repas"}, {"rendez-vous"}}');
ERROR: multidimensional arrays must have array expressions with
matching dimensions

La syntaxe du constructeur ARRAY peut aussi être utilisée :

INSERT INTO sal_emp


VALUES ('Bill',
ARRAY[10000, 10000, 10000, 10000],
ARRAY[['rendez-vous', 'repas'],
['entrainement','présentation']]);

INSERT INTO sal_emp


VALUES ('Carol',
ARRAY[20000, 25000, 25000, 25000],
ARRAY[['petit-déjeuner', 'consultation'], ['rendez-vous',
'repas']]);

Les éléments du tableau sont des constantes SQL ordinaires ou des expressions ; par exemple, les
chaînes de caractères littérales sont encadrées par des guillemets simples au lieu de guillemets doubles
comme cela est le cas dans un tableau littéral. La syntaxe du constructeur ARRAY est discutée plus
en profondeur dans la Section 4.2.12.

8.15.3. Accès aux tableaux


Quelques requêtes lancées sur la table permettent d'éclairer le propos précédent. Tout d'abord, l'accès
à un seul élément du tableau. Cette requête retrouve le nom des employés dont la paye a changé au
cours de la deuxième semaine :

SELECT nom FROM sal_emp WHERE paye_par_semaine[1] <>


paye_par_semaine[2];

nom
-------
Carol
(1 row)

Les indices du tableau sont écrits entre crochets. Par défaut, PostgreSQL utilise la convention des
indices commençant à 1 pour les tableaux, c'est-à-dire un tableau à n éléments commence avec
array[1] et finit avec array[n].

Récupérer la paye de la troisième semaine de tous les employés :

SELECT paye_par_semaine[3] FROM sal_emp;

paye_par_semaine
------------------
10000
25000
(2 rows)

Il est également possible d'accéder à des parties rectangulaires arbitraires ou à des sous-tableaux. Une
partie de tableau est indiquée par l'écriture extrémité basse:extrémité haute sur n'importe
quelle dimension. Ainsi, la requête suivante retourne le premier élément du planning de Bill pour les
deux premiers jours de la semaine :

187
Types de données

SELECT planning[1:2][1:1] FROM sal_emp WHERE nom = 'Bill';

planning
--------------------
{{rendez-vous},{entrainement}}
(1 row)

Si l'une des dimensions est écrite comme une partie, c'est-à-dire si elle contient le caractère deux-
points, alors toutes les dimensions sont traitées comme des parties. Toute dimension qui n'a qu'un
numéro (pas de deux-points), est traitée comme allant de 1 au nombre indiqué. Par exemple, [2] est
traitée comme [1:2], comme le montre cet exemple :

SELECT planning[1:2][2] FROM sal_emp WHERE nom = 'Bill';

planning
---------------------------
{{rendez-vous,repas},{entrainement,présentation}}
(1 row)

Pour éviter la confusion avec le cas sans indice, il est préférable d'utiliser la syntaxe avec indice pour
toutes les dimensions, c'est-à-dire [1:2][1:1] et non pas [2][1:1].

Il est possible d'omettre la limite basse et/ou la limite haute dans les indices. La limite
manquante est remplacée par la limite basse ou haute des dimensions du tableau. Par exemple :

SELECT planning[:2][2:] FROM sal_emp WHERE nom = 'Bill';

planning
------------------------
{{lunch},{presentation}}
(1 row)

SELECT planning[:][1:1] FROM sal_emp WHERE nom = 'Bill';

schedule
------------------------
{{meeting},{training}}
(1 row)

Une expression indicée de tableau retourne NULL si le tableau ou une des expressions est NULL.
De plus, NULL est renvoyé si un indice se trouve en dehors de la plage du tableau (ce cas
n'amène pas d'erreur). Par exemple, si planning a les dimensions [1:3][1:2], faire référence à
planning[3][3] donne un résultat NULL. De la même façon, une référence sur un tableau avec
une valeur d'indices incorrecte retourne une valeur NULL plutôt qu'une erreur.

Une expression de découpage d'un tableau est aussi NULL si, soit le tableau, soit une des expressions
indicées est NULL. Néanmoins, dans certains cas particuliers comme la sélection d'une partie d'un
tableau complètement en dehors de la plage de ce dernier, l'expression de cette partie est un tableau
vide (zéro dimension) et non pas un tableau NULL. (Ceci ne correspond pas au comportement sans
indice, et est fait pour des raisons historiques.) Si la partie demandée surcharge partiellement les limites
du tableau, alors elle est réduite silencieusement à la partie surchargée au lieu de renvoyer NULL.

Les dimensions actuelles de toute valeur de type tableau sont disponibles avec la fonction
array_dims :

SELECT array_dims(planning) FROM sal_emp WHERE nom = 'Carol';

array_dims

188
Types de données

------------
[1:2][1:2]
(1 row)

array_dims donne un résultat de type text, ce qui est pratique à lire, mais peut s'avérer
plus difficile à interpréter par les programmes. Les dimensions sont aussi récupérables avec
array_upper et array_lower, qui renvoient respectivement la limite haute et la limite basse
du tableau précisé :

SELECT array_upper(planning, 1) FROM sal_emp WHERE nom = 'Carol';

array_upper
-------------
2
(1 row)

array_length renverra la longueur de la dimension indiquée pour le tableau :

SELECT array_length(planning, 1) FROM sal_emp WHERE nom = 'Carol';

array_length
--------------
2
(1 row)

cardinality renvoie le nombre total d'éléments d'un tableau sur toutes ses dimensions. Autrement
dit, c'est le nombre de lignes que renverrait un appel à la fonction unnest :

SELECT cardinality(planning) FROM sal_emp WHERE nom = 'Carol';

cardinality
-------------
4
(1 row)

8.15.4. Modification de tableaux


La valeur d'un tableau peut être complètement remplacée :

UPDATE sal_emp SET paye_par_semaine = '{25000,25000,27000,27000}'


WHERE nom = 'Carol';

ou en utilisant la syntaxe de l'expression ARRAY :

UPDATE sal_emp SET paye_par_semaine =


ARRAY[25000,25000,27000,27000]
WHERE nom = 'Carol';

On peut aussi mettre à jour un seul élément d'un tableau :

UPDATE sal_emp SET paye_par_semaine[4] = 15000


WHERE nom = 'Bill';

ou faire une mise à jour par tranche :

UPDATE sal_emp SET paye_par_semaine[1:2] = '{27000,27000}'

189
Types de données

WHERE nom = 'Carol';

Les syntaxes des indices avec la limite basse et/ou la limite upper-bound omise peuvent
aussi être utilisées lors de la mise à jour d'une valeur d'un tableau qui est différent de NULL ou à plus
de zéro dimension (sinon, il n'existe pas de limite à substituer).

Un tableau peut être agrandi en y stockant des éléments qui n'y sont pas déjà présents. Toute position
entre ceux déjà présents et les nouveaux éléments est remplie avec la valeur NULL. Par exemple, si le
tableau mon_tableau a actuellement quatre éléments, il en aura six après une mise à jour qui affecte
mon_tableau[6], car mon_tableau[5] est alors rempli avec une valeur NULL. Actuellement,
l'agrandissement de cette façon n'est autorisé que pour les tableaux à une dimension, pas pour les
tableaux multidimensionnels.

L'affectation par parties d'un tableau permet la création de tableaux dont l'indice de départ n'est pas 1.
On peut ainsi affecter, par exemple, mon_tableau[-2:7] pour créer un tableau avec des valeurs
d'indices allant de -2 à 7.

Les valeurs de nouveaux tableaux peuvent aussi être construites en utilisant l'opérateur de
concaténation, || :

SELECT ARRAY[1,2] || ARRAY[3,4];


?column?
---------------
{1,2,3,4}
(1 row)

SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];


?column?
---------------------
{{5,6},{1,2},{3,4}}
(1 row)

L'opérateur de concaténation autorise un élément à être placé au début ou à la fin d'un tableau à une
dimension. Il accepte aussi deux tableaux à N dimensions, ou un tableau à N dimensions et un à N
+1 dimensions.

Quand un élément seul est poussé soit au début soit à la fin d'un tableau à une dimension, le résultat
est un tableau avec le même indice bas que l'opérande du tableau. Par exemple :

SELECT array_dims(1 || '[0:1]={2,3}'::int[]);


array_dims
------------
[0:2]
(1 row)

SELECT array_dims(ARRAY[1,2] || 3);


array_dims
------------
[1:3]
(1 row)

Lorsque deux tableaux ayant un même nombre de dimensions sont concaténés, le résultat conserve
la limite inférieure de l'opérande gauche. Le résultat est un tableau comprenant chaque élément de
l'opérande gauche suivi de chaque élément de l'opérande droit. Par exemple :

SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]);


array_dims
------------
[1:5]
(1 row)

190
Types de données

SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]);


array_dims
------------
[1:5][1:2]
(1 row)

Lorsqu'un tableau à N dimensions est placé au début ou à la fin d'un tableau à N+1 dimensions, le
résultat est analogue au cas ci-dessus. Chaque sous-tableau de dimension N est en quelque sorte un
élément de la dimension externe d'un tableau à N+1 dimensions. Par exemple :

SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);


array_dims
------------
[1:3][1:2]
(1 row)

Un tableau peut aussi être construit en utilisant les fonctions array_prepend, array_append
ou array_cat. Les deux premières ne supportent que les tableaux à une dimension alors que
array_cat supporte les tableaux multidimensionnels. Quelques exemples :

SELECT array_prepend(1, ARRAY[2,3]);


array_prepend
---------------
{1,2,3}
(1 row)

SELECT array_append(ARRAY[1,2], 3);


array_append
--------------
{1,2,3}
(1 row)

SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);


array_cat
---------------
{1,2,3,4}
(1 row)

SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);


array_cat
---------------------
{{1,2},{3,4},{5,6}}
(1 row)

SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);


array_cat
---------------------
{{5,6},{1,2},{3,4}}

Dans les cas simples, l'opération de concaténation discutée ci-dessus est préférée à l'utilisation directe
de ces fonctions. Néanmoins, comme l'opérateur de concaténation est surchargé pour servir les trois
cas, certaines utilisations peuvent bénéficier de l'utilisation d'une fonction pour éviter toute ambiguïté.
Par exemple :

SELECT ARRAY[1, 2] || '{3, 4}'; -- le littéral non typé est pris


pour un tableau
?column?

191
Types de données

-----------
{1,2,3,4}

SELECT ARRAY[1, 2] || '7'; -- idem pour celui-ci


ERROR: malformed array literal: "7"

SELECT ARRAY[1, 2] || NULL; -- pareil pour un NULL


?column?
----------
{1,2}
(1 row)

SELECT array_append(ARRAY[1, 2], NULL); -- ceci peut être voulu


array_append
--------------
{1,2,NULL}

Dans l'exemple ci-dessus, l'analyseur voit un tableau d'entiers d'un côté de l'opérateur de concaténation
et une constante de type indéterminé de l'autre. L'heuristique utilisée pour résoudre le type de la
constante revient à assumer qu'elle est de même type que l'autre entrée de l'opérateur -- dans ce cas,
un tableau d'entiers. Donc, l'opérateur de concaténation est supposé représenter array_cat, et non
pas array_append. Quand le choix est erroné, cela peut se corriger en convertissant la constante
dans le type de données d'un élément du tableau. L'utilisation de la fonction array_append peut
être préférable.

8.15.5. Recherche dans les tableaux


Pour rechercher une valeur dans un tableau, il faut vérifier chaque valeur dans le tableau. Ceci peut se
faire à la main lorsque la taille du tableau est connue. Par exemple :

SELECT * FROM sal_emp WHERE paye_par_semaine[1] = 10000 OR


paye_par_semaine[2] = 10000 OR
paye_par_semaine[3] = 10000 OR
paye_par_semaine[4] = 10000;

Ceci devient toutefois rapidement fastidieux pour les gros tableaux et n'est pas très utile si la taille
du tableau n'est pas connue. Une autre méthode est décrite dans la Section 9.23. La requête ci-dessus
est remplaçable par :

SELECT * FROM sal_emp WHERE 10000 = ANY (paye_par_semaine);

De la même façon, on trouve les lignes où le tableau n'a que des valeurs égales à 10000 avec :

SELECT * FROM sal_emp WHERE 10000 = ALL (paye_par_semaine);

Sinon, la fonction generate_subscripts peut être utilisée. Par exemple :

SELECT * FROM
(SELECT paye_par_semaine,
generate_subscripts(paye_par_semaine, 1) AS s
FROM sal_emp) AS foo
WHERE paye_par_semaine[s] = 10000;

Cette fonction est décrite dans Tableau 9.59.

Vous pouvez aussi chercher dans un tableau en utilisant l'opérateur &&, qui vérifie si l'opérande gauche
a des éléments communs avec l'opérande droit. Par exemple :

192
Types de données

SELECT * FROM sal_emp WHERE paye_par_semaine && ARRAY[10000];

Les opérateurs sur les tableaux sont décrits plus en profondeur dans Section 9.18. Leurs performances
peuvent profiter d'un index approprié, comme décrit dans Section 11.2.

Vous pouvez aussi rechercher des valeurs spécifiques dans un tableau en utilisant les fonctions
array_position et array_positions. La première renvoie l'indice de la première occurrence
d'une valeur dans un tableau. La seconde renvoie un tableau avec les indices de toutes les occurrences
de la valeur dans le tableau. Par exemple :

SELECT
array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'],
'mon');
array_positions
-----------------
2

SELECT array_positions(ARRAY[1, 4, 3, 1, 3, 4, 2, 1], 1);


array_positions
-----------------
{1,4,8}

Astuce
Les tableaux ne sont pas des ensembles ; rechercher des éléments spécifiques dans un tableau
peut être un signe d'une mauvaise conception de la base de données. On utilise plutôt une
table séparée avec une ligne pour chaque élément faisant partie du tableau. Cela simplifie la
recherche et fonctionne mieux dans le cas d'un grand nombre d'éléments.

8.15.6. Syntaxe d'entrée et de sortie des tableaux


La représentation externe du type texte d'une valeur de type tableau consiste en des éléments interprétés
suivant les règles de conversion d'entrées/sorties pour le type de l'élément du tableau, plus des
décorations indiquant la structure du tableau. L'affichage est constitué d'accolades ({ et }) autour des
valeurs du tableau et de caractères de délimitation entre éléments adjacents. Le caractère délimiteur
est habituellement une virgule (,) mais peut différer : il est déterminé par le paramètre typdelim
du type de l'élément tableau. Parmi les types de données standards supportés par l'implantation de
PostgreSQL, seul le type box utilise un point-virgule (;), tous les autres utilisant la virgule. Dans
un tableau multidimensionnel, chaque dimension (row, plane, cube, etc.) utilise son propre niveau
d'accolades et les délimiteurs doivent être utilisés entre des entités adjacentes au sein d'accolades de
même niveau.

La routine de sortie du tableau place des guillemets doubles autour des valeurs des éléments si ce
sont des chaînes vides, si elles contiennent des accolades, des caractères délimiteurs, des guillemets
doubles, des antislashs ou des espaces ou si elles correspondent à NULL. Les guillemets doubles et
les antislashs intégrés aux valeurs des éléments sont échappés à l'aide d'un antislash. Pour les types de
données numériques, on peut supposer sans risque que les doubles guillemets n'apparaissent jamais,
mais pour les types de données texte, il faut être préparé à gérer la présence et l'absence de guillemets.

Par défaut, la valeur de la limite basse d'un tableau est initialisée à 1. Pour représenter des tableaux
avec des limites basses différentes, les indices du tableau doivent être indiqués explicitement avant
d'écrire le contenu du tableau. Cet affichage est constitué de crochets ([]) autour de chaque limite
basse et haute d'une dimension avec un délimiteur deux-points (:) entre les deux. L'affichage des
dimensions du tableau est suivi par un signe d'égalité (=). Par exemple :

193
Types de données

SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2


FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1)
AS ss;

e1 | e2
----+----
1 | 6
(1 row)

La routine de sortie du tableau inclut les dimensions explicites dans le résultat uniquement lorsqu'au
moins une limite basse est différente de 1.

Si la valeur écrite pour un élément est NULL (toute variante), l'élément est considéré NULL. La
présence de guillemets ou d'antislashs désactive ce fonctionnement et autorise la saisie de la valeur
littérale de la chaîne « NULL ». De plus, pour une compatibilité ascendante avec les versions
antérieures à la version 8.2 de PostgreSQL, le paramètre de configuration array_nulls doit être
désactivé (off) pour supprimer la reconnaissance de NULL comme un NULL.

Comme indiqué précédemment, lors de l'écriture d'une valeur de tableau, des guillemets doubles
peuvent être utilisés autour de chaque élément individuel du tableau. Il faut le faire si leur absence
autour d'un élément induit en erreur l'analyseur de tableau. Par exemple, les éléments contenant des
crochets, virgules (ou tout type de données pour le caractère délimiteur correspondant), guillemets
doubles, antislashs ou espace (en début comme en fin) doivent avoir des guillemets doubles. Les
chaînes vides et les chaînes NULL doivent aussi être entre guillemets. Pour placer un guillemet
double ou un antislash dans une valeur d'élément d'un tableau, faites le précéder d'un antislash.
Alternativement, il est possible de se passer de guillemets et d'utiliser l'échappement par antislash pour
protéger tous les caractères de données qui seraient autrement interprétés en tant que caractères de
syntaxe de tableau.

Des espaces peuvent être ajoutées avant un crochet gauche ou après un crochet droit. Comme avant
tout élément individuel. Dans tous ces cas-là, les espaces sont ignorées. En revanche, les espaces à
l'intérieur des éléments entre guillemets doubles ou entourées de caractères autres que des espaces ne
sont pas ignorées.

Astuce
La syntaxe du constructeur ARRAY (voir Section 4.2.12) est souvent plus facile à utiliser que
la syntaxe de tableau littéral lors de l'écriture des valeurs du tableau en commandes SQL. Avec
ARRAY, les valeurs de l'élément individuel sont écrites comme elles le seraient si elles ne
faisaient pas partie d'un tableau.

8.16. Types composites


Un type composite représente la structure d'une ligne ou d'un enregistrement ; il est en essence une
simple liste de noms de champs et de leurs types de données. PostgreSQL autorise l'utilisation de types
composites identiques de plusieurs façons à l'utilisation des types simples. Par exemple, une colonne
d'une table peut être déclarée comme étant de type composite.

8.16.1. Déclaration de types composites


Voici deux exemples simples de définition de types composites :

CREATE TYPE complexe AS (


r double precision,

194
Types de données

i double precision
);

CREATE TYPE element_inventaire AS (


nom text,
id_fournisseur integer,
prix numeric
);

La syntaxe est comparable à CREATE TABLE, sauf que seuls les noms de champs et leurs types
peuvent être spécifiés ; aucune contrainte (telle que NOT NULL) ne peut être incluse actuellement.
Notez que le mot-clé AS est essentiel ; sans lui, le système penserait à un autre genre de commande
CREATE TYPE et vous obtiendriez d'étranges erreurs de syntaxe.

Après avoir défini les types, nous pouvons les utiliser pour créer des tables :

CREATE TABLE disponible (


element element_inventaire,
nombre integer
);

INSERT INTO disponible VALUES (ROW('fuzzy dice', 42, 1.99), 1000);

ou des fonctions :

CREATE FUNCTION prix_extension(element_inventaire, integer) RETURNS


numeric
AS 'SELECT $1.prix * $2' LANGUAGE SQL;

SELECT prix_extension(element, 10) FROM disponible;

Quand vous créez une table, un type composite est automatiquement créé, avec le même nom que la
table, pour représenter le type de ligne de la table. Par exemple, si nous avions dit :

CREATE TABLE element_inventaire (


nom text,
id_fournisseur integer REFERENCES fournisseur,
prix numeric CHECK (prix > 0)
);

alors le même type composite element_inventaire montré ci-dessus aurait été créé et pourrait
être utilisé comme ci-dessus. Néanmoins, notez une restriction importante de l'implémentation
actuelle : comme aucune contrainte n'est associée avec un type composite, les contraintes indiquées
dans la définition de la table ne sont pas appliquées aux valeurs du type composite en dehors de la table.
(Pour contourner ceci, créer un domaine sur le type composite, et appliquer les contraintes désirées
en tant que contraintes CHECK du domaine.)

8.16.2. Construire des valeurs composites


Pour écrire une valeur composite comme une constante littérale, englobez les valeurs du champ dans
des parenthèses et séparez-les par des virgules. Vous pouvez placer des guillemets doubles autour de
chaque valeur de champ et vous devez le faire si elle contient des virgules ou des parenthèses (plus de
détails ci-dessous). Donc, le format général d'une constante composite est le suivant :

'( val1 , val2 , ... )'

Voici un exemple :

'("fuzzy dice",42,1.99)'

195
Types de données

qui serait une valeur valide du type element_inventaire défini ci-dessus. Pour rendre un champ
NULL, n'écrivez aucun caractère dans sa position dans la liste. Par exemple, cette constante spécifie
un troisième champ NULL :

'("fuzzy dice",42,)'

Si vous voulez un champ vide au lieu d'une valeur NULL, saisissez deux guillemets :

'("",42,)'

Ici, le premier champ est une chaîne vide non NULL alors que le troisième est NULL.

(Ces constantes sont réellement seulement un cas spécial de constantes génériques de type discutées
dans la Section 4.1.2.7. La constante est initialement traitée comme une chaîne et passée à la routine de
conversion de l'entrée de type composite. Une spécification explicite de type pourrait être nécessaire
pour préciser le type à utiliser pour la conversion de la constante.)

La syntaxe d'expression ROW pourrait aussi être utilisée pour construire des valeurs composites. Dans
la plupart des cas, ceci est considérablement plus simple à utiliser que la syntaxe de chaîne littérale,
car vous n'avez pas à vous inquiéter des multiples couches de guillemets. Nous avons déjà utilisé cette
méthode ci-dessus :

ROW('fuzzy dice', 42, 1.99)


ROW('', 42, NULL)

Le mot-clé ROW est optionnel si vous avez plus d'un champ dans l'expression, donc ceci peut être
simplifié avec

('fuzzy dice', 42, 1.99)


('', 42, NULL)

La syntaxe de l'expression ROW est discutée avec plus de détails dans la Section 4.2.13.

8.16.3. Accéder aux types composites


Pour accéder à un champ d'une colonne composite, vous pouvez écrire un point et le nom du champ, un
peu comme la sélection d'un champ à partir d'un nom de table. En fait, c'est tellement similaire que vous
pouvez souvent utiliser des parenthèses pour éviter une confusion de l'analyseur. Par exemple, vous
pouvez essayer de sélectionner des sous-champs à partir de notre exemple de table, disponible,
avec quelque chose comme :

SELECT element.nom FROM disponible WHERE element.prix > 9.99;

Ceci ne fonctionnera pas, car le nom element est pris pour le nom d'une table, et non pas d'une
colonne de disponible, suivant les règles de la syntaxe SQL. Vous devez l'écrire ainsi :

SELECT (element).nom FROM disponible WHERE (element).prix > 9.99;

ou si vous avez aussi besoin d'utiliser le nom de la table (par exemple dans une requête multitable),
de cette façon :

SELECT (disponible.element).nom FROM disponible WHERE


(disponible.element).prix > 9.99;

Maintenant, l'objet entre parenthèses est correctement interprété comme une référence à la colonne
element, puis le sous-champ peut être sélectionné à partir de lui.

Des problèmes syntaxiques similaires s'appliquent quand vous sélectionnez un champ à partir d'une
valeur composite. En fait, pour sélectionner un seul champ à partir du résultat d'une fonction renvoyant
une valeur composite, vous aurez besoin d'écrire quelque chose comme :

SELECT (ma_fonction(...)).champ FROM ...

196
Types de données

Sans les parenthèses supplémentaires, ceci provoquera une erreur.

Le nom du champ spécial * signifie « tous les champs », comme expliqué dans Section 8.16.5.

8.16.4. Modifier les types composites


Voici quelques exemples de la bonne syntaxe pour insérer et mettre à jour des colonnes composites.
Tout d'abord, pour insérer ou modifier une colonne entière :

INSERT INTO matab (col_complexe) VALUES((1.1,2.2));

UPDATE matab SET col_complexe = ROW(1.1,2.2) WHERE ...;

Le premier exemple omet ROW, le deuxième l'utilise ; nous pouvons le faire des deux façons.

Nous pouvons mettre à jour un sous-champ individuel d'une colonne composite :

UPDATE matab SET col_complexe.r = (col_complexe).r + 1 WHERE ...;

Notez ici que nous n'avons pas besoin de (et, en fait, ne pouvons pas) placer des parenthèses autour
des noms de colonnes apparaissant juste après SET, mais nous avons besoin de parenthèses lors de la
référence à la même colonne dans l'expression à droite du signe d'égalité.

Et nous pouvons aussi spécifier des sous-champs comme cibles de la commande INSERT :

INSERT INTO matab (col_complexe.r, col_complexe.i) VALUES(1.1,


2.2);

Si tous les sous-champs d'une colonne ne sont pas spécifiés, ils sont remplis avec une valeur NULL.

8.16.5. Utiliser des types composites dans les


requêtes
Il existe différentes règles spéciales de syntaxe et de différents comportements associés avec les types
composites dans les requêtes. Ces règles fournissent des raccourcis utiles, mais peuvent être difficiles
à appréhender si vous ne connaissez pas la logique qui y est associée.

Dans PostgreSQL, une référence à un nom de table (ou à un alias) dans une requête est réellement
une référence au type composite de la ligne courante de la table. Par exemple, si nous avons une table
element_inventaire comme définie ci-dessus, nous pouvons écrire :

SELECT c FROM element_inventaire c;

Cette requête renvoie une seule colonne comprenant une valeur composite, et nous pourrions obtenir
l'affichage suivant :

c
------------------------
("fuzzy dice",42,1.99)
(1 row)

Il faut noter néanmoins que les noms simples (c.-à-d. sans qualifiant) sont traités comme des noms de
colonnes puis comme des noms de table s'il n'y a pas de correspondance avec les noms de colonnes.
Donc cet exemple fonctionne seulement parce qu'il n'existe pas de colonne nommée c dans les tables
de la requête.

197
Types de données

La syntaxe habituelle avec des noms de colonne qualifiés (comme nom_table.nom_colonne)


peut se comprendre en appliquant la sélection de champs à la valeur composite de la ligne actuelle de
la table. (Pour des raisons d'efficacité, ce n'est pas réellement implémenté de cette façon.)

Quand nous écrivons

SELECT c.* FROM element_inventaire c;

alors, d'après le standard SQL, nous devrions obtenir le contenu de la table étendu en des colonnes
séparées :

nom | id_fournisseur | prix


------------+----------------+-------
fuzzy dice | 42 | 1.99
(1 row)

comme si la requête avait été écrite ainsi :

SELECT c.nom, c.id_fournisseur, c.prix FROM element_inventaire c;

PostgreSQL appliquera ce comportement étendu à toute expression de valeur composite, bien que,
comme indiqué ci-dessus, il est nécessaire d'ajouter des parenthèses autour de la valeur à qui .* est
appliquée à chaque fois qu'il ne s'agit pas d'un nom de table. Par exemple, si ma_fonction() est
une fonction renvoyant un type composite avec les colonnes a, b et c, alors ces deux requêtes donnent
le même résultat :

SELECT (ma_fonction(x)).* FROM une_table;


SELECT (ma_fonction(x)).a, (ma_fonction(x)).b, (ma_fonction(x)).c
FROM une_table;

Astuce
PostgreSQL gère le fait d'étendre les colonnes en transformant la première forme en la seconde.
De ce fait, dans cet exemple, ma_fonction() serait appelé trois fois par ligne, quelle que
soit la syntaxe utilisée. S'il s'agit d'une fonction peu performante, vous pourriez souhaiter éviter
cela, ce que vous pouvez faire avec une requête de ce type :

SELECT (m).* FROM (SELECT ma_fonction(x) AS m FROM une_table


OFFSET 0) ss;

Placer la fonction dans un élément LATERAL du FROM l'aide à ne pas être invoquée plus d'une
fois par ligne. m.* est toujours étendu en m.a, m.b, m.c, mais maintenant ces variables
sont juste des références à la sortie de l'élément FROM. (Le mot-clé LATERAL est optionnel
ici, mais nous le montrons pour clarifier que la fonction obtient x de la some_table.)

La syntaxe valeur_composite.* étend les colonnes avec un résultat de ce type quand il apparaît
au niveau haut d'une liste en sortie du SELECT, d'une liste RETURNING dans des commandes
INSERT/UPDATE/DELETE, d'une clause VALUES, ou d'un constructeur de ligne. Dans tous les autres
contextes (incluant l'imbrication dans une de ces constructions), attacher .* à une valeur composite

198
Types de données

value ne change pas la valeur, car cela signifie « toutes les colonnes » et donc la valeur composite est
produite de nouveau. Par exemple, si une_fonction() accepte un argument de valeur composite,
ces requêtes ont un résultat identique :

SELECT une_fonction(c.*) FROM element_inventaire c;


SELECT une_fonction(c) FROM elemen