在Postgres 9.0+中使用PL / pgSQL循环表

时间:2013-03-07 08:26:37

标签: postgresql variables loops plpgsql tablename

我想循环遍历所有表来计算每个表中的行数。以下查询给我一个错误:

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 个答案:

答案 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系统表。