postgresql 找出基于表值的方案

jhdbpxl9  于 2023-02-08  发布在  PostgreSQL
关注(0)|答案(3)|浏览(113)

我的数据库根据客户端分为多个模式(即:每个客户端具有它们自己的具有相同数据结构的模式)。
我还碰巧有一个外部操作不知道它应该以哪个模式为目标。它来自系统的另一个部分,该部分没有客户机的概念,也不知道它正在哪个客户机集中操作。在处理它之前,我必须找出请求需要以哪个模式为目标
为了找到正确的模式,我必须找出哪个模式保存了具有特定唯一ID(字符串)的记录R
据我了解,以下

SET search_path TO schema1,schema2,schema3,...

将只查看schema1(或与表匹配的第一个模式)中的表,而不执行全局搜索。
有没有一种方法可以对所有模式进行全局搜索,或者我只需要使用for循环,一次迭代一个模式?

c2e8gylq

c2e8gylq1#

为此,您可以使用inheritance(请务必考虑限制)。
请看这个小演示:

CREATE SCHEMA master;  -- no access of others ..

CREATE SEQUENCE master.myseq;  -- global sequence for globally unique ids

CREATE table master.tbl (
  id int primary key DEFAULT nextval('master.myseq')
, foo text);

CREATE SCHEMA x;
CREATE table x.tbl() INHERITS (master.tbl);
INSERT INTO  x.tbl(foo) VALUES ('x');

CREATE SCHEMA y;
CREATE table y.tbl() INHERITS (master.tbl);
INSERT INTO  y.tbl(foo) VALUES ('y');

SELECT * FROM x.tbl;  -- returns 'x'
SELECT * FROM y.tbl;  -- returns 'y'
SELECT * FROM master.tbl;  -- returns 'x' and 'y' <-- !!

现在,要实际标识特定行所在的表,请使用tableoid

SELECT *, tableoid::regclass AS table_name
FROM   master.tbl
WHERE  id = 2;

结果:

id | foo | table_name
---+-----+-----------
2  | y   | y.tbl

您可以从tableoid导出源模式,最好直接用tableoid查询系统目录(显示的名称取决于search_path的设置)。

SELECT n.nspname 
FROM   master.tbl   t
JOIN   pg_class     c ON c.oid = t.tableoid
JOIN   pg_namespace n ON c.relnamespace = n.oid
WHERE  t.id = 2;

这也比遍历许多单独的表快得多。

t2a7ltrp

t2a7ltrp2#

你必须遍历所有的名称空间,你可以从pg_*系统目录中获得大量的信息,理论上,你应该能够在请求时解析客户机-〉模式Map,而不需要与数据库对话,这样你所做的第一个SQL调用是:

SET search_path = client1,global_schema;
e1xvtsh3

e1xvtsh33#

虽然我认为如果可以重新构建表,Erwin的解决方案可能更可取,但另一种不需要任何模式更改的方法是编写一个PL/PgSQL函数,该函数基于系统目录信息使用动态SQL扫描表。
给定:

CREATE SCHEMA a;
CREATE SCHEMA b;

CREATE TABLE a.testtab ( searchval text );
CREATE TABLE b.testtab (LIKE a.testtab);

INSERT INTO a.testtab(searchval) VALUES ('ham');
INSERT INTO b.testtab(searchval) VALUES ('eggs');

以下PL/PgSQL函数在包含名为_tabname的表的所有方案中搜索_colname中等于_value的值,并返回第一个匹配的方案。

CREATE OR REPLACE FUNCTION find_schema_for_value(_tabname text, _colname text, _value text) RETURNS text AS $$
DECLARE
    cur_schema text;
    foundval integer;
BEGIN
    FOR cur_schema IN 
      SELECT nspname 
      FROM pg_class c 
      INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) 
      WHERE c.relname = _tabname AND c.relkind = 'r'
    LOOP
      EXECUTE 
        format('SELECT 1 FROM %I.%I WHERE %I = $1', 
            cur_schema, _tabname, _colname
        ) INTO foundval USING _value;
      IF foundval = 1 THEN
        RETURN cur_schema;
      END IF;
    END LOOP;
    RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';

如果没有匹配则返回null。如果有多个匹配则结果将是其中之一,但不保证是哪一个。如果要返回(比如说)字母顺序的第一个或其他什么。如果你想返回 * all *,这个函数也被简单地修改为返回setof textRETURN NEXT cur_schema火柴。

regress=# SELECT find_schema_for_value('testtab','searchval','ham');
 find_schema_for_value 
-----------------------
 a
(1 row)

regress=# SELECT find_schema_for_value('testtab','searchval','eggs');
 find_schema_for_value 
-----------------------
 b
(1 row)

regress=# SELECT find_schema_for_value('testtab','searchval','bones');
 find_schema_for_value 
-----------------------

(1 row)

顺便说一句,如果愿意,您可以重用没有继承的表定义,而且您确实应该这样做。

CREATE TYPE public.testtab AS ( searchval text );
CREATE TABLE a.testtab OF public.testtab;
CREATE TABLE b.testtab OF public.testtab;

在这种情况下它们共享相同的数据类型但不共享任何数据;或或通过LIKE

CREATE TABLE public.testtab ( searchval text );
CREATE TABLE a.testtab (LIKE public.testtab);
CREATE TABLE b.testtab (LIKE public.testtab);

在这种情况下,它们在创建后彼此完全不连接。

相关问题