Je veux parcourir toutes mes tables pour compter les lignes dans chacune d'elles. La requête suivante m'obtient une erreur:
DO $$
DECLARE
tables CURSOR FOR
SELECT tablename FROM pg_tables
WHERE tablename NOT LIKE 'pg_%'
ORDER BY tablename;
tablename varchar(100);
nbRow int;
BEGIN
FOR tablename IN tables LOOP
EXECUTE 'SELECT count(*) FROM ' || tablename INTO nbRow;
-- Do something with nbRow
END LOOP;
END$$;
Les erreurs:
ERROR: syntax error at or near ")" LINE 1: SELECT count(*) FROM (sql_features) ^ QUERY: SELECT count(*) FROM (sql_features) CONTEXT: PL/pgSQL function inline_code_block line 8 at EXECUTE statement
sql_features
Est le nom d'une table dans ma base de données. J'ai déjà essayé d'utiliser quote_ident()
mais en vain.
Le curseur renvoie un enregistrement, pas une valeur scalaire, donc "nom_table" n'est pas une variable chaîne.
La concaténation transforme l'enregistrement en une chaîne qui ressemble à ceci (sql_features)
. Si vous aviez sélectionné par exemple le schéma avec le nom de la table, la représentation textuelle de l'enregistrement aurait été (public,sql_features)
.
Vous devez donc accéder à la colonne à l'intérieur de l'enregistrement pour créer votre instruction SQL:
DO $$
DECLARE
tables CURSOR FOR
SELECT tablename
FROM pg_tables
WHERE tablename NOT LIKE 'pg_%'
ORDER BY tablename;
nbRow int;
BEGIN
FOR table_record IN tables LOOP
EXECUTE 'SELECT count(*) FROM ' || table_record.tablename INTO nbRow;
-- Do something with nbRow
END LOOP;
END$$;
Vous voudrez peut-être utiliser WHERE schemaname = 'public'
au lieu de not like 'pg_%'
pour exclure les tables système Postgres.
Je ne me souviens pas de la dernière fois où j'avais réellement besoin d'utiliser un curseur explicite pour faire une boucle dans plpgsql.
Utilisez le curseur implicite d'une boucle FOR
, c'est beaucoup plus propre:
DO
$$
DECLARE
rec record;
nbrow bigint;
BEGIN
FOR rec IN
SELECT *
FROM pg_tables
WHERE tablename NOT LIKE 'pg\_%'
ORDER BY tablename
LOOP
EXECUTE 'SELECT count(*) FROM '
|| quote_ident(rec.schemaname) || '.'
|| quote_ident(rec.tablename)
INTO nbrow;
-- Do something with nbrow
END LOOP;
END
$$;
Vous devez inclure le nom du schéma pour que cela fonctionne pour tous les schémas (y compris ceux qui ne figurent pas dans votre search_path
).
De plus, vous avez réellement besoin d'utiliser quote_ident()
ou format()
avec %I
Pour se protéger contre l'injection SQL. Un nom de table peut être presque n'importe quoi entre guillemets doubles.
Petit détail: échapper le trait de soulignement (_
) Dans le modèle LIKE
pour en faire un littéral trait de soulignement: tablename NOT LIKE 'pg\_%'
DO
$$
DECLARE
tbl regclass;
nbrow bigint;
BEGIN
FOR tbl IN
SELECT c.oid
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT LIKE 'pg\_%' -- system schema(s)
AND n.nspname <> 'information_schema' -- information schema
ORDER BY n.nspname, c.relname
LOOP
EXECUTE 'SELECT count(*) FROM ' || tbl INTO nbrow;
-- raise notice '%: % rows', tbl, nbrow;
END LOOP;
END
$$;
Requête pg_catalog.pg_class
au lieu de tablename
, elle fournit le OID de la table.
Le type d'identifiant d'objet regclass
est pratique pour simplifier, en particulier, les noms de table sont entre guillemets et qualifiés de schéma si nécessaire automatiquement (empêche également injection SQL ).
Cette requête exclut également les tables temporaires (le schéma temporaire est nommé avec pg_temp%
En interne).
Si vous ne souhaitez que des tables d'un schéma donné:
AND n.nspname = 'public' -- schema name here, case-sensitive