2

以下に示す関数find_value_in_table()で、列の値が文字列「255.255.255.255」と一致するレコードを持つ列の名前を見つけようとしています。

要するに、このようなもの:

SELECT column_name
  FROM dynamic_table
  WHERE column_value = '255.255.255.255';

ノート:

  • テーブルの名前はパラメータとして関数に渡されるため、「dynamic_table」
  • データ値ではなく、列の名前を判別しようとしています。

これが最初のステップです。後で、column_valueもパラメーター化します。値を格納しているテーブルがあることを知っているので、"255.255.255.255"この値を格納しているテーブルと列の名前を見つけて、この関数の機能を証明したいと思います。

このすべての目的:私は大きなデータベースを持っており、それをレトロエンジニアリングしています。私はそれがどこかに、アプリケーションのいくつかの構成パラメーターを含んでいることを知っています。現在の構成では、これらのパラメーターのいくつかの正確な値を知っています(アプリケーション構成GUIに表示されます:コンピューター名、IPアドレスなど)。この構成情報を格納しているテーブルを特定するために、データベース全体を参照したいと思います。

find_value()私はこれらの手がかりを返す関数を構築してきました。

これはどのように行うことができますか?

create or replace function find_all_columns(tablename in text) 
    return setof record as
$func$
    declare r record;
    begin 
        return select   a.attname as "Column",
            pg_catalog.format_type(a.atttypid, a.atttypmod) as "Datatype"
            from
            pg_catalog.pg_attribute a
            where
            a.attnum > 0
            and not a.attisdropped
            and a.attrelid = (  select c.oid from pg_catalog.pg_class c left join pg_catalog.pg_namespace n on n.oid = c.relnamespace where c.relname ~ '^('  ||   quote_ident(tablename)    ||   ')$' and pg_catalog.pg_table_is_visible(c.oid);
        end loop;
    end;
$func$ language 'plpgsql';  

create or replace function find_value_in_table(tablename text) 
    returns setof record as 
$func$
    declare r record;
        return select 
    begin 

        for r in (select find_all_columns(tablename)) loop
            return select * from tablename t where t... = "255.255.255.255" /*  here column would be the value in the record: r.Column*/ 
        end loop;
    end;
$func$ language 'plpgsql';

create or replace function find_tables_name(_username text)
  returns setof record as
$func$
declare
   tbl text;
begin
   for tbl in 
      select t.tablename from pg_tables t
      where  t.tableowner = _username and t.schemaname = 'public'
   loop
      return quote_ident(tbl);
   end loop;
end;
$func$ language 'plpgsql';

create or replace function find_value(_username text, valuetofind text) 
    returns setof record as 
$func$
    declare r record;
    begin 
        for r in (select find_tables_name(_username)) loop
            return find_value_in_table( r.tablename );
        end loop;
    end;
$func$ language 'plpgsql';
4

1 に答える 1

3

これを実現するための基本的な方法の1つは、プレーンテキストのダンプを作成し、選択したエディター(私の場合はvim)を使用して文字列を検索することです。

しかし、この関数はより良い仕事をします。:)

CREATE OR REPLACE FUNCTION find_columns(_owner text
                                       ,_valuetofind text
                                       ,_part bool = FALSE)
  RETURNS TABLE (tbl text, col text, typ text) LANGUAGE plpgsql STRICT AS
$func$
DECLARE
   _go         bool;
   _search_row text := '%' || _search || '%';  -- Search row for part of string
BEGIN
   IF _part THEN   -- search col for part of string?
      valuetofind := '%' || valuetofind || '%';
   END IF;

FOR tbl IN 
   SELECT quote_ident(t.schemaname) || '.' || quote_ident(t.tablename)
   FROM pg_tables t
   WHERE  t.tableowner = _owner
-- AND t.schemaname = 'public' -- uncomment to only search one schema
LOOP
   EXECUTE '
   SELECT EXISTS (
      SELECT 1 FROM ' || tbl || ' t WHERE t::text ~~ $1)' -- check whole row
   INTO  _go
   USING _search_row;

   IF _go THEN
      FOR col, typ IN 
         SELECT quote_ident(a.attname) -- AS col
               ,pg_catalog.format_type(a.atttypid, a.atttypmod) -- AS typ
         FROM   pg_catalog.pg_attribute a
         WHERE  a.attnum > 0
         AND    NOT a.attisdropped
         AND    a.attrelid = tbl::regclass
      LOOP
         EXECUTE '
         SELECT EXISTS (
            SELECT 1
            FROM ' || tbl || ' WHERE ' || col || '::text ~~ $1)' -- check col
         INTO   _go
         USING  valuetofind;

         IF _go THEN
            RETURN NEXT;
         END IF;
      END LOOP;
   END IF;

END LOOP;
END;
$func$;
COMMENT ON FUNCTION x.find_columns(text, text, boolean) IS 'Search all tables
 owned by "_owner" user for a value "_search" (text representation).
 Match full or partial (_part)';

電話:

SELECT * FROM find_columns('postgres', '255.255.255.255');
SELECT * FROM find_columns('fadmin', '255.255.255.255', TRUE);

戻り値:

       tbl       |     col     | typ
-----------------+-------------+------
 event.eventkat  | eventkat    | text
 public.foo      | description | text
 public.bar      | filter      | text

PostgreSQL9.1でテスト済み

主なポイント

  • 機能はワンストップショップです。

  • 値の一部を検索するオプションを作成しました(_part)。デフォルトでは、列全体を検索します。

  • 行全体にクイックテストを組み込んで、テーブルがまったく含まれていないテーブルを削除しvaluetofindました。このために、行全体をテキストにすばやく変換するPostgreSQLの機能を使用します。これにより、関数が大幅に高速化されます。ただし、すべてまたはほとんどすべてのテーブルが適格である場合、またはテーブルに列が1つしかない場合を除きます。

  • リターンタイプをとして定義し、暗黙的に定義されRETURNS TABLE (tbl text, col text, typ text)た変数を割り当てtblます。したがって、追加の変数は必要なく、列が修飾されるとすぐに実行できます。coltypRETURN NEXT

  • ここを多用してEXISTSください!列に値があるかどうかだけに関心があるので、これが最速のオプションです

  • LIKE正規表現の代わりに(または~~略して)使用します。よりシンプルに、より速く。

  • 私はquote_ident()すべての識別子をすぐに。

  • EXECUTE *command* INTO USINGインストルメンタルです。

于 2012-08-25T04:41:41.563 に答える