30

すべてのテーブルをループして、それぞれの行をカウントしたいと考えています。次のクエリでエラーが発生します。

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私のDBのテーブルの名前です。私はすでに使用しようとしましquote_ident()たが、役に立ちませんでした。

4

2 に答える 2

37

最後に、plpgsql でループするために明示カーソルを実際に使用する必要があったのはいつか思い出せません。loop
の暗黙のカーソルを使用します。これははるかにクリーンです。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

また、 SQL インジェクションから保護するために、実際にはorを変数と一緒に使用する必要があります。テーブル名は、二重引用符内のほとんど何でもかまいません。見る:quote_ident()format()%Iregclass

マイナーな詳細:パターン_内のアンダースコア ( ) をエスケープして、リテラルアンダースコアにします。LIKEtablename 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は単純化するのに便利です。特に、テーブル名は二重引用符で囲まれ、必要に応じて自動的にスキーマ修飾されます ( SQL インジェクションも防止します)。

このクエリでは、一時テーブルも除外されます (一時スキーマはpg_temp%内部で命名されます)。

特定のスキーマのテーブルのみを含めるには:

    AND    n.nspname = 'public' -- schema name here, case-sensitive
于 2013-03-07T18:29:35.477 に答える
30

カーソルはスカラー値ではなくレコードを返すため、「テーブル名」は文字列変数ではありません。

連結により、レコードは次のような文字列になります(sql_features)。たとえば、スキーマ名とテーブル名を選択した場合、レコードのテキスト表現は(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$$;

Postgres システム テーブルを除外するWHERE schemaname = 'public'代わりに使用したい場合があります。not like 'pg_%'

于 2013-03-07T08:46:10.410 に答える