在下面提供的函数find_value_in_table()
中,我试图找到任何列的名称,这些列的列值与字符串“255.255.255.255”匹配。
简而言之,就像这样:
SELECT column_name
FROM dynamic_table
WHERE column_value = '255.255.255.255';
注意:
这是第一步。稍后我也会参数化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';
答案 0 :(得分:3)
实现这一目标的一种基本方法是进行纯文本转储并使用您选择的编辑器(在我的情况下为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
使用PostgreSQL 9.1进行测试
该功能是一站式服务。
我构建了一个选项来搜索部分值(_part
)。默认设置是搜索整列。
我在整行上进行了快速测试,以消除表中根本没有valuetofind
的表格。我使用PostgreSQL能够快速将整行转换为文本。这应该使函数更快 - 除非所有或几乎所有表都符合条件或表只有一列。
我将返回类型定义为RETURNS TABLE (tbl text, col text, typ text)
,并立即分配隐式定义的变量tbl
,col
和typ
。所以我不需要额外的变量,当列符合条件时,我可以立即RETURN NEXT
。
在这里大量使用EXISTS
!这是最快的选择,因为您只关心该列是否具有值。
使用LIKE
(或简称~~
)代替正则表达式。更简单,更快。
我quote_ident()
立即使用所有标识符。