Fonctions utiles#
Nous regroupons ici quelques fonctions réalisées au cours de formations ou d'accompagnements d'utilisateurs de PostgreSQL.
Ajout de l'auto-incrémentation sur un champ entier#
Lorsqu'on importe une couche dans une table via les outils de QGIS, le champ d'identifiant choisi n'a pas le support de l'auto-incrémentation, ce qui peut poser des problèmes de l'ajout de nouvelles données.
Depuis PostgreSQL 10, on peut maintenant utiliser des identités au lieu des serial pour avoir un champ auto-complété. Voir par exemple l'article https://www.loxodata.com/post/identity/
Pour ajouter le support de l'auto-incrémentation sur un champ entier à une table existante, on peut utiliser les commandes suivantes :
1 2 3 4 5 |
|
Pour transformer les séquences créées précédemment via des serial
en identité avec identity
, on peut lancer :
1 2 3 4 5 6 7 8 9 10 11 |
|
Création automatique d'indexes spatiaux#
Pour des données spatiales volumineuses, les performances d'affichage sont bien meilleures à grande échelle si on a ajouté un index spatial. L'index est aussi beaucoup utilisé pour améliorer les performances d'analyses spatiales.
On peut créer l'index spatial table par table, ou bien automatiser cette création, c'est-à-dire créer les indexes spatiaux pour toutes les tables qui n'en ont pas.
Pour cela, nous avons conçu une fonction, téléchargeable ici: https://gist.github.com/mdouchin/cfa0e37058bcf102ed490bc59d762042
On doit copier/coller le script SQL de cette page GIST
dans la fenêtre SQL
du Gestionnaire de bases de données de QGIS, puis lancer la requête avec Exécuter.
On peut ensuite vider le contenu de la fenêtre, puis appeler la fonction create_missing_spatial_indexes
via le code SQL suivant :
1 2 3 4 5 6 7 8 |
|
Trouver toutes les tables sans clé primaire#
Il est très important de déclarer une clé primaire pour vos tables stockées dans PostgreSQL. Cela fournit un moyen aux logiciels comme QGIS d'identifier de manière performante les lignes dans une table. Sans clé primaire, les performances d'accès aux données peuvent être dégradées.
Vous pouvez trouver l'ensemble des tables de votre base de données sans clé primaire en construisant cette vue PostgreSQL tables_without_primary_key
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
- Pour lister les tables sans clé primaire, vous pouvez ensuite lancer la requête suivante:
1 2 |
|
Ce qui peut donner par exemple:
table_schema | table_name |
---|---|
agriculture | parcelles |
agriculture | puits |
cadastre | sections |
environnement | znieff |
environnement | parcs_naturels |
- Pour lister les tables sans clé primaire dans un schéma particulier, par exemple
cadastre
, vous pouvez ensuite lancer la requête :
1 2 3 |
|
Ce qui peut alors donner:
table_schema | table_name |
---|---|
cadastre | sections |
Ajouter automatiquement plusieurs champs à plusieurs tables#
Il est parfois nécessaire d'ajouter des champs à une ou plusieurs tables, par exemple pour y stocker ensuite des métadonnées (date de modification, date d'ajout, utilisateur, lien, etc).
Nous proposons pour cela la fonction ajout_champs_dynamiques
qui permet de fournir un nom de schéma, un nom de table, et une chaîne de caractère contenant la liste séparée par virgule des champs et de leur type.
La fonction est accessible ici: https://gist.github.com/mdouchin/50234f1f33801aed6f4f2cbab9f4887c
- Exemple d'utilisation pour une table
commune
du schématest
: on ajoute les champsdate_creation
,date_modification
etutilisateur
1 2 3 |
|
- Exemple d'utilisation pour toutes les tables d'un schéma, ici le schéma
test
. On utilise dans cette exemple la vuegeometry_columns
qui liste les tables spatiales, car on souhaite aussi ne faire cet ajout que pour les données de type POINT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Vérifier la taille des bases, tables et schémas#
Connaître la taille des bases de données#
On peut lancer la requête suivante, qui renvoie les bases de données ordonnées par taille descendante.
1 2 3 4 5 6 7 |
|
Calculer la taille des tables#
On crée une fonction get_table_info
qui utilise les tables système pour lister les tables, récupérer leur schéma et les informations de taille.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
On peut l'utiliser simplement de la manière suivante
1 2 3 4 5 |
|
Calculer la taille des schémas#
On crée une simple fonction qui renvoie la somme des tailles des tables d'un schéma
1 2 3 4 5 6 7 8 9 10 |
|
On peut alors l'utiliser pour connaître la taille d'un schéma
1 2 |
|
Ou lister l'ensemble des schémas
1 2 3 4 5 |
|
Lister les triggers appliqués sur les tables#
On peut utiliser la requête suivante pour lister l'ensemble des triggers activés sur les tables
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Cette requête renvoie un tableau de la forme :
table_schema | table_name | trigger_schema | trigger_name | event | activation | condition | trigger_active | definition |
---|---|---|---|---|---|---|---|---|
gestion | acteur | gestion | tr_date_maj | UPDATE | BEFORE | f | EXECUTE FUNCTION occtax.maj_date() | |
occtax | organisme | occtax | tr_date_maj | UPDATE | BEFORE | t | EXECUTE FUNCTION occtax.maj_date() | |
taxon | iso_metadata_reference | taxon | update_imr_timestamp | UPDATE | BEFORE | t | EXECUTE FUNCTION taxon.update_imr_timestamp_column() |
Lister les fonctions installées par les extensions#
Il est parfois utile de lister les fonctions des extensions, par exemple pour :
- vérifier leur nom et leurs paramètres.
- détecter celles qui n'ont pas le bon propriétaire
La requête suivante permet d'afficher les informations essentielles des fonctions créées par les extensions installées dans la base :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
qui renvoie une résultat comme ceci (cet exemple est un extrait de quelques lignes) :
extension_schema | extension_name | function_schema | function_name | function_params | function_owner |
---|---|---|---|---|---|
public | fuzzystrmatch | public | levenshtein_less_equal | text, text, integer | johndoe |
public | fuzzystrmatch | public | metaphone | text, integer | johndoe |
public | fuzzystrmatch | public | soundex | text | johndoe |
public | fuzzystrmatch | public | text_soundex | text | johndoe |
public | hstore | public | akeys | hstore | johndoe |
public | hstore | public | avals | hstore | johndoe |
public | hstore | public | defined | hstore, text | johndoe |
public | postgis | public | st_buffer | text, double precision, integer | johndoe |
public | postgis | public | st_buffer | geom geometry, radius double precision, options text | johndoe |
public | postgis | public | st_buildarea | geometry | johndoe |
On peut bien sûr modifier la clause WHERE
pour filtrer plus ou moins les fonctions renvoyées.
Lister les vues contenant row_number() over()
non typé en integer
#
Si on utilise des vues dans QGIS qui créent un identifiant unique via le numéro de ligne, il est important :
- que le type de cet identifiant soit entier
integer
et pas entier longbigint
- avoir une clause
ORDER BY
pour essayer au maximum que QGIS récupère les objets toujours dans le même ordre.
Quand une requête d'une vue utilise row_number() OVER()
, depuis des versions récentes de PostgreSQL, cela renvoie un entier long bigint
ce qui n'est pas conseillé.
On peut trouver ces vues ou vues matérialisées via cette requête :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Lister les tables qui ont une clé primaire non entière#
Pour éviter des soucis de performances sur les gros jeux de données, il faut éviter d'avoir des tables avec des clés primaires sur des champs qui ne sont pas de type entier integer
.
En effet, dans QGIS, l'ouverture de ce type de table avec une clé primaire de type text
, ou même bigint
, cela entraîne la création et le stockage en mémoire d'une table de correspondance entre chaque objet de la couche et le numéro d'arrivée de la ligne. Sur les tables volumineuses, cela peut être sensible.
Pour trouver toutes les tables, on peut faire cette requête :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Ce qui donne par exemple :
table_schema | table_name | column_name | column_type |
---|---|---|---|
un_schema | une_table_a | id | bigint |
un_schema | une_table_b | id | bigint |
un_autre_schema | autre_table_c | id | character varying |
un_autre_schema | autre_table_d | id | character varying |
Trouver les tables spatiales avec une géométrie non typée#
Il est important lorsqu'on crée des champs de type géométrie geometry
de préciser le type des objets (point, ligne, polygone, etc.) et la projection.
On doit donc créer les champs comme ceci :
1 2 3 4 |
|
et non comme ceci :
1 2 3 4 |
|
C'est donc important lorsqu'on crée des tables à partir de requêtes SQL de toujours bien typer les géométries. Par exemple :
1 2 3 4 5 6 |
|
On peut trouver toutes les tables qui auraient été créées avec des champs de géométrie non typés via la requête suivante :
1 2 3 4 |
|
Il faut corriger ces vues ou tables.
Trouver les objets avec des géométries trop complexes#
1 2 3 4 |
|
Les trop gros polygones (zones inondables, zonages issus de regroupement de nombreux objets, etc.) peuvent poser de réels soucis de performance, notamment sur les opérations d'intersection avec les objets d'autres couches via ST_Intersects
.
On peut corriger cela via la fonction ST_Subdivide
. Voir Documentation de ST_Subdivide
Tester les différences entre 2 tables de même structure#
Nous souhaitons comparer deux tables de la base, par exemple une table de communes en 2021 communes_2021
et une table de communes en 2022 communes_2022
.
On peut utiliser une fonction qui utilise les possibilités du format hstore pour comparer les données entre elles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
Cette fonction attend en paramètres
- le schéma de la table A. Ex:
referentiels
- le nom de la table A. Ex:
communes_2021
- le schéma de la table B. Ex:
referentiels
- le nom de la table B. Ex:
communes_2022
- le nom du champ qui identifie de manière unique la donnée. Ce n'est pas forcément la clé primaire. Ex
code_commune
- un tableau de champs pour lesquels ne pas vérifier les différences. Ex:
array['region', 'departement']
La requête à lancer est la suivantes
1 2 3 4 5 6 7 8 9 |
|
Exemple de données renvoyées:
uid | status | table_a_values | table_b_values |
---|---|---|---|
12345 | not in table A | NULL | "annee_ref"=>"2022", "nom_commune"=>"Nouvelle commune", "population"=>"5723" |
97612 | not in table B | "annee_ref"=>"2021", "nom_commune"=>"Ancienne commune", "population"=>"840" | NULL |
97602 | table A != table B | "annee_ref"=>"2021", "population"=>"1245" | "annee_ref"=>"2022", "population"=>"1322" |
Dans l'affichage ci-dessus, je n'ai pas affiché le champ de géométrie, mais la fonction teste aussi les différences de géométries.
Attention, les performances de ce type de requête ne sont pas forcément assurées pour des volumes de données importants.
Trouver les valeurs distinctes des champs d'une table#
Pour comprendre quelles données sont présentes dans une table PostgreSQL,
vous pouvez exploiter la puissance des fonctions de manipulation du JSON
et récupérer automatiquement toutes les valeurs distinctes d'une table.
Cela permet de lister les champs de cette table et de bien se représenter ce qu'ils contiennent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
ce qui donnera comme résultat
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Points d'attention:
- Attention aux performances sur un très gros volume de données.
- Bien penser à ne pas prendre en compte les champs qui contiennent des données différentes pour tous les objets (identifiants, longueur, etc.) au risque d'avoir une très longue liste de valeurs uniques.
Continuer vers Gestion des droits