Ik kan me niet herinneren wanneer ik voor het laatst een expliciete cursor moest gebruiken voor looping in plpgsql.
Gebruik de impliciete cursor van een FOR
lus, dat is veel schoner:
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
$$;
U moet de schemanaam opnemen om dit voor alle schema's te laten werken (inclusief diegene die niet in uw search_path
staan ).
Ook heb je eigenlijk nodig om quote_ident()
te gebruiken of format()
met %I
of een regclass
variabele om te beschermen tegen SQL-injectie. Een tabelnaam kan bijna alles zijn tussen dubbele aanhalingstekens. Zie:
- Tabelnaam als een PostgreSQL-functieparameter
Klein detail:escape the underscore (_
) in de LIKE
patroon om er een letterlijk van te maken underscore:tablename NOT LIKE 'pg\_%'
Hoe ik het zou kunnen doen:
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
$$;
Zoekopdracht pg_catalog.pg_class
in plaats van tablename
, het geeft de OID van de tafel.
Het object-ID type regclass
is handig om te vereenvoudigen. In het bijzonder worden tabelnamen dubbel geciteerd en waar nodig automatisch gekwalificeerd voor het schema (verhindert ook SQL-injectie).
Deze query sluit ook tijdelijke tabellen uit (tijdelijk schema heet pg_temp%
intern).
Om alleen tabellen van een bepaald schema op te nemen:
AND n.nspname = 'public' -- schema name here, case-sensitive