Compter le nombre d'enregistrements de chaque table en Postgres
En SQL, une des premières choses qu'on apprend est de compter le nombre d'enregistrements présents dans une table unique à l'aide d'un simple count(*). Quand j'ai eu à transférer la totalité des enregistrements des tables d'une base de données existante vers une nouvelle, je me suis aperçu que sur la centaine de tables, quelques enregistrements avaient été perdus en chemin. Par exemple, si une table source comptait 1000 inscriptions, la table de destination n'en recevait que 999.
Pour dépister rapidement les tables qui ne balançaient pas, mon réflexe fût de bâtir une requête qui listerait toutes les tables d'un utilisateur propriétaire et pour chacune, de lancer une sous-requête qui obtiendrait le compte. Le problème, c'est qu'on ne peut pas le faire par un seul énoncé SQL. Bien que pg_tables retourne le schéma et le nom de la table, il faut contourner la limitation en écrivant une fonction utilitaire qui les concaténerait sous forme de texte pour être exécutée comme équivalent à la sous-requête qui retournerait le compte.
Sous cette forme :
SELECT schemaname, tablename, count_table_rows(schemaname, tablename) as nb_rows FROM pg_tables WHERE tableowner='code18' ORDER BY schemaname, tablename
La définition de la fonction PL/pgSQL de ma solution :
CREATE OR REPLACE FUNCTION count_table_rows ( "schema_name" name, "table_name" name ) RETURNS integer AS $body$ DECLARE nb_rows INTEGER; BEGIN EXECUTE 'SELECT COUNT(*) FROM ' || schema_name || '.' || table_name INTO nb_rows; RETURN nb_rows; END; $body$ LANGUAGE 'plpgsql'Vous remarquerez que j'ai déclaré les paramètres en utilisant le type "name" plutôt que "text" ou "varchar". C'est qu'en testant la fonction sur deux serveurs qui faisaient tourner des versions différentes de Postgres, l'un procédait à un cast implicite à partir d'un varchar, l'autre d'un text. Pour que la fonction soit compatible dans les deux cas sans nécessiter de conversion, j'ai vérifié quels étaient les types des valeurs retournées par pg_tables et j'ai ajusté la fonction avec des paramètres de type name :
SELECT column_name, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'pg_tables'