我想循环遍历所有表来计算每个表中的行数。以下查询给我一个错误:
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()
,但无济于事。
答案 0 :(得分:25)
我不记得上次我真的需要在plpgsql中使用显式游标进行循环
使用FOR
loop的隐式光标,它更清晰:
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()
or 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。
object identifier type regclass
方便简化,特别是表名是双引号并且必要时会自动进行模式限定(也会阻止SQL injection)。
此查询还排除了临时表(临时表在内部以pg_temp%
命名)。
如果您只想要来自给定架构的表:
AND n.nspname = 'public' -- schema name here, case-sensitive
答案 1 :(得分:14)
光标返回一条记录,而不是一个标量值,所以&#34; tablename&#34;不是字符串变量。
连接将记录转换为类似于(sql_features)
的字符串。如果您选择了例如带有tablename的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系统表。