Postgres 查找数据库表中与给定列上的条件匹配的所有行
Postgres find all rows in database tables matching criteria on a given column
我正在尝试编写子查询,以便在所有 table 中搜索名为 id
的列,因为有多个 table 带有 id
列,我想添加条件,以便 id = 3119093
.
我的尝试是:
Select *
from information_schema.tables
where id = '3119093' and id IN (
Select table_name
from information_schema.columns
where column_name = 'id' );
这没有用,所以我尝试了:
Select *
from information_schema.tables
where table_name IN (
Select table_name
from information_schema.columns
where column_name = 'id' and 'id' IN (
Select * from table_name where 'id' = 3119093));
这也不是正确的方法。任何帮助,将不胜感激。谢谢!
更难的尝试是:
CREATE OR REPLACE FUNCTION search_columns(
needle text,
haystack_tables name[] default '{}',
haystack_schema name[] default '{public}'
)
RETURNS table(schemaname text, tablename text, columnname text, rowctid text)
AS $$
begin
FOR schemaname,tablename,columnname IN
SELECT c.table_schema,c.table_name,c.column_name
FROM information_schema.columns c
JOIN information_schema.tables t ON
(t.table_name=c.table_name AND t.table_schema=c.table_schema)
WHERE (c.table_name=ANY(haystack_tables) OR haystack_tables='{}')
AND c.table_schema=ANY(haystack_schema)
AND t.table_type='BASE TABLE'
--AND c.column_name = "id"
LOOP
EXECUTE format('SELECT ctid FROM %I.%I WHERE cast(%I as text) like %L',
schemaname,
tablename,
columnname,
needle
) INTO rowctid;
IF rowctid is not null THEN
RETURN NEXT;
END IF;
END LOOP;
END;
$$ language plpgsql;
select * from search_columns('%3119093%'::varchar,'{}'::name[]) ;
唯一的问题是此代码显示 table 名称和列名称。然后我必须手动输入
Select * from table_name where id = 3119093
我从上面的代码中得到 table 名称的地方。
我想自动实现从 table 返回行,但我不知道如何自动获取 table 名称。
我花时间让它为你工作。
对于初学者,一些关于代码内部情况的信息。
说明
- 函数有两个输入参数:列名和列值
- 它需要一个创建的类型,它将返回一组
- 第一个循环识别 table 具有指定为输入参数的列名称的
- 然后它形成一个查询,该查询聚合从步骤 3 中获取的每个 table 中与输入条件匹配的所有行,并根据
ILIKE
进行比较 - 根据您的示例
- 仅当当前访问的 table 中至少有一行符合指定条件(则数组不为空)时,函数才会进入第二个循环
- 第二个循环取消嵌套与条件匹配的行数组,并将每个元素放入带有
RETURN NEXT rec
子句的函数输出中
备注
使用 LIKE 搜索效率低下 - 我建议添加另一个输入参数 "column type" 并通过向 pg_catalog.pg_type
table 添加连接来限制它在查找中。
存在第二个循环,因此如果为特定 table 找到超过 1 行,则返回每一行。
如果您正在寻找其他东西,比如您需要键值对,而不仅仅是值,那么您需要扩展该功能。例如,您可以从行构建 json 格式。
现在,进入代码。
测试用例
CREATE TABLE tbl1 (col1 int, id int); -- does contain values
CREATE TABLE tbl2 (col1 int, col2 int); -- doesn't contain column "id"
CREATE TABLE tbl3 (id int, col5 int); -- doesn't contain values
INSERT INTO tbl1 (col1, id)
VALUES (1, 5), (1, 33), (1, 25);
Table 存储数据:
postgres=# select * From tbl1;
col1 | id
------+----
1 | 5
1 | 33
1 | 25
(3 rows)
创建类型
CREATE TYPE sometype AS ( schemaname text, tablename text, colname text, entirerow text );
函数代码
CREATE OR REPLACE FUNCTION search_tables_for_column (
v_column_name text
, v_column_value text
)
RETURNS SETOF sometype
LANGUAGE plpgsql
STABLE
AS
$$
DECLARE
rec sometype%rowtype;
v_row_array text[];
rec2 record;
arr_el text;
BEGIN
FOR rec IN
SELECT
nam.nspname AS schemaname
, cls.relname AS tablename
, att.attname AS colname
, null::text AS entirerow
FROM
pg_attribute att
JOIN pg_class cls ON att.attrelid = cls.oid
JOIN pg_namespace nam ON cls.relnamespace = nam.oid
WHERE
cls.relkind = 'r'
AND att.attname = v_column_name
LOOP
EXECUTE format('SELECT ARRAY_AGG(row(tablename.*)::text) FROM %I.%I AS tablename WHERE %I::text ILIKE %s',
rec.schemaname, rec.tablename, rec.colname, quote_literal(concat('%',v_column_value,'%'))) INTO v_row_array;
IF v_row_array is not null THEN
FOR rec2 IN
SELECT unnest(v_row_array) AS one_row
LOOP
rec.entirerow := rec2.one_row;
RETURN NEXT rec;
END LOOP;
END IF;
END LOOP;
END
$$;
示例调用和输出
postgres=# select * from search_tables_for_column('id','5');
schemaname | tablename | colname | entirerow
------------+-----------+---------+-----------
public | tbl1 | id | (1,5)
public | tbl1 | id | (1,25)
(2 rows)
我正在尝试编写子查询,以便在所有 table 中搜索名为 id
的列,因为有多个 table 带有 id
列,我想添加条件,以便 id = 3119093
.
我的尝试是:
Select *
from information_schema.tables
where id = '3119093' and id IN (
Select table_name
from information_schema.columns
where column_name = 'id' );
这没有用,所以我尝试了:
Select *
from information_schema.tables
where table_name IN (
Select table_name
from information_schema.columns
where column_name = 'id' and 'id' IN (
Select * from table_name where 'id' = 3119093));
这也不是正确的方法。任何帮助,将不胜感激。谢谢!
更难的尝试是:
CREATE OR REPLACE FUNCTION search_columns(
needle text,
haystack_tables name[] default '{}',
haystack_schema name[] default '{public}'
)
RETURNS table(schemaname text, tablename text, columnname text, rowctid text)
AS $$
begin
FOR schemaname,tablename,columnname IN
SELECT c.table_schema,c.table_name,c.column_name
FROM information_schema.columns c
JOIN information_schema.tables t ON
(t.table_name=c.table_name AND t.table_schema=c.table_schema)
WHERE (c.table_name=ANY(haystack_tables) OR haystack_tables='{}')
AND c.table_schema=ANY(haystack_schema)
AND t.table_type='BASE TABLE'
--AND c.column_name = "id"
LOOP
EXECUTE format('SELECT ctid FROM %I.%I WHERE cast(%I as text) like %L',
schemaname,
tablename,
columnname,
needle
) INTO rowctid;
IF rowctid is not null THEN
RETURN NEXT;
END IF;
END LOOP;
END;
$$ language plpgsql;
select * from search_columns('%3119093%'::varchar,'{}'::name[]) ;
唯一的问题是此代码显示 table 名称和列名称。然后我必须手动输入
Select * from table_name where id = 3119093
我从上面的代码中得到 table 名称的地方。
我想自动实现从 table 返回行,但我不知道如何自动获取 table 名称。
我花时间让它为你工作。
对于初学者,一些关于代码内部情况的信息。
说明
- 函数有两个输入参数:列名和列值
- 它需要一个创建的类型,它将返回一组
- 第一个循环识别 table 具有指定为输入参数的列名称的
- 然后它形成一个查询,该查询聚合从步骤 3 中获取的每个 table 中与输入条件匹配的所有行,并根据
ILIKE
进行比较 - 根据您的示例 - 仅当当前访问的 table 中至少有一行符合指定条件(则数组不为空)时,函数才会进入第二个循环
- 第二个循环取消嵌套与条件匹配的行数组,并将每个元素放入带有
RETURN NEXT rec
子句的函数输出中
备注
使用 LIKE 搜索效率低下 - 我建议添加另一个输入参数 "column type" 并通过向
pg_catalog.pg_type
table 添加连接来限制它在查找中。存在第二个循环,因此如果为特定 table 找到超过 1 行,则返回每一行。
如果您正在寻找其他东西,比如您需要键值对,而不仅仅是值,那么您需要扩展该功能。例如,您可以从行构建 json 格式。
现在,进入代码。
测试用例
CREATE TABLE tbl1 (col1 int, id int); -- does contain values
CREATE TABLE tbl2 (col1 int, col2 int); -- doesn't contain column "id"
CREATE TABLE tbl3 (id int, col5 int); -- doesn't contain values
INSERT INTO tbl1 (col1, id)
VALUES (1, 5), (1, 33), (1, 25);
Table 存储数据:
postgres=# select * From tbl1;
col1 | id
------+----
1 | 5
1 | 33
1 | 25
(3 rows)
创建类型
CREATE TYPE sometype AS ( schemaname text, tablename text, colname text, entirerow text );
函数代码
CREATE OR REPLACE FUNCTION search_tables_for_column (
v_column_name text
, v_column_value text
)
RETURNS SETOF sometype
LANGUAGE plpgsql
STABLE
AS
$$
DECLARE
rec sometype%rowtype;
v_row_array text[];
rec2 record;
arr_el text;
BEGIN
FOR rec IN
SELECT
nam.nspname AS schemaname
, cls.relname AS tablename
, att.attname AS colname
, null::text AS entirerow
FROM
pg_attribute att
JOIN pg_class cls ON att.attrelid = cls.oid
JOIN pg_namespace nam ON cls.relnamespace = nam.oid
WHERE
cls.relkind = 'r'
AND att.attname = v_column_name
LOOP
EXECUTE format('SELECT ARRAY_AGG(row(tablename.*)::text) FROM %I.%I AS tablename WHERE %I::text ILIKE %s',
rec.schemaname, rec.tablename, rec.colname, quote_literal(concat('%',v_column_value,'%'))) INTO v_row_array;
IF v_row_array is not null THEN
FOR rec2 IN
SELECT unnest(v_row_array) AS one_row
LOOP
rec.entirerow := rec2.one_row;
RETURN NEXT rec;
END LOOP;
END IF;
END LOOP;
END
$$;
示例调用和输出
postgres=# select * from search_tables_for_column('id','5');
schemaname | tablename | colname | entirerow
------------+-----------+---------+-----------
public | tbl1 | id | (1,5)
public | tbl1 | id | (1,25)
(2 rows)