Цикл на таблицах с PL / pgSQL в Postgres 9.0+
Я хочу перебрать все мои таблицы, чтобы подсчитать строки в каждом из них. Следующий запрос возвращает мне ошибку:
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$$;
ошибки:
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
- имя таблицы в моей БД. Я уже пытался использовать quote_ident()
но безрезультатно.
2 ответов
курсор возвращает запись, а не скалярное значение, поэтому "tablename" не является строковой переменной.
конкатенация превращает запись в строку, которая выглядит следующим образом (sql_features)
. Если бы вы выбрали, например, schemaname с именем таблицы, текстовое представление записи было бы (public,sql_features)
.
поэтому вам нужно получить доступ к столбцу внутри записи, чтобы создать инструкцию 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$$;
вы можете использовать WHERE schemaname = 'public'
вместо not like 'pg_%'
чтобы исключить системные таблицы Postgres.
я не могу вспомнить последний раз, когда мне действительно нужно было использовать явный курсор для цикла в plpgsql.
Используйте неявный курсор FOR
цикл, это намного чище:
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
$$;
вам нужно включить имя схемы, чтобы сделать эту работу для всех схем (включая те, которые не в вашем search_path
).
кроме того, вы на самом деле нужно использовать quote_ident()
или format()
С %I
для защиты от SQL-инъекций. Имя таблицы может быть почти все внутри двойных кавычек.
незначительные детали: избежать подчеркивания (_
) в LIKE
шаблон, чтобы сделать его литерал подчеркивание: 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
$$;
запрос
pg_catalog.pg_class
вместоtablename
, он обеспечивает OID таблицы.на объект с идентификатором типа
regclass
is удобно для упрощения, в частности, имена таблиц имеют двойные кавычки и схему, где это необходимо автоматически (также предотвращает SQL-инъекций).этот запрос также исключает временные таблицы (временная схема называется с
pg_temp%
внутренне).-
если вы хотите только таблицы из данной схемы:
AND n.nspname = 'public' -- schema name here, case-sensitive