Postgres 查找数据库表中与给定列上的条件匹配的所有行

1 postgresql subquery multiple-tables multiple-columns

我正在尝试编写子查询,以便在所有表中搜索名为的列id,并且由于有多个带有列的表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' );
Run Code Online (Sandbox Code Playgroud)

这不起作用,所以我尝试:

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));
Run Code Online (Sandbox Code Playgroud)

这也不是正确的方法。任何帮助,将不胜感激。谢谢!

更难的尝试是:

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[]) ;
Run Code Online (Sandbox Code Playgroud)

唯一的问题是此代码显示表名和列名。然后我必须手动输入

Select * from table_name where id = 3119093
Run Code Online (Sandbox Code Playgroud)

我从上面的代码中获取了表名。

我想自动实现从表中返回行,但我不知道如何自动获取表名。

Kam*_*ski 5

我花了时间让它为你工作。

首先,了解一些有关代码内部发生的情况的信息。

解释

  1. 函数接受两个输入参数:列名和列值
  2. 它需要一个创建的类型,它将返回一组
  3. 第一个循环标识将列名指定为输入参数的表
  4. 然后它形成一个查询,该查询聚合与步骤 3 中获取的每个表中的输入条件相匹配的所有行,并基于ILIKE- 根据您的示例进行比较
  5. 仅当当前访问的表中至少有一行符合指定条件(则数组不为空)时,函数才会进入第二个循环
  6. 第二个循环解除与条件匹配的行数组的嵌套,并将每个元素放入函数输出 withRETURN NEXT rec子句中

笔记

  • 使用 LIKE 搜索效率低下 - 我建议添加另一个输入参数“列类型”,并通过添加到表的联接来限制它在查找中pg_catalog.pg_type

  • 第二个循环是这样,如果特定表找到多于 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);
Run Code Online (Sandbox Code Playgroud)

表存储数据:

postgres=# select * From tbl1;

 col1 | id
------+----
    1 |  5
    1 | 33
    1 | 25
(3 rows)
Run Code Online (Sandbox Code Playgroud)

创建类型

CREATE TYPE sometype AS ( schemaname text, tablename text, colname text, entirerow text );
Run Code Online (Sandbox Code Playgroud)

功能码

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
$$;
Run Code Online (Sandbox Code Playgroud)

示例调用和输出

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)
Run Code Online (Sandbox Code Playgroud)