Jag kommer inte ihåg när jag senast behövde använda en explicit markör för att loopa i plpgsql.
Använd den implicita markören för en FOR
loop, det är mycket renare:
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
$$;
Du måste inkludera schemanamnet för att detta ska fungera för alla scheman (inklusive de som inte finns i din search_path
).
Dessutom behöver du för att använda quote_ident()
eller format()
med %I
eller en regclass
variabel för att skydda mot SQL-injektion. Ett tabellnamn kan vara nästan vad som helst inuti dubbla citattecken. Se:
- Tabellnamn som en PostgreSQL-funktionsparameter
Mindre detalj:Undvik understrecket (_
) i LIKE
mönster för att göra det bokstavligt understreck:tablename NOT LIKE 'pg\_%'
Hur jag kan göra det:
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
$$;
Fråga pg_catalog.pg_class
istället för tablename
, den tillhandahåller tabellens OID.
Objektidentifieraren typ regclass
är praktiskt att förenkla. Speciellt tabellnamn är dubbla citattecken och schemakvalificerade vid behov automatiskt (förhindrar också SQL-injektion).
Den här frågan exkluderar även tillfälliga tabeller (tempschemat heter pg_temp%
internt).
För att endast inkludera tabeller från ett givet schema:
AND n.nspname = 'public' -- schema name here, case-sensitive