4

我的数据库根据客户端分为模式(即:每个客户端都有自己的模式,具有相同的数据结构)。

我也碰巧有一个不知道它应该针对哪个模式的外部操作。它来自系统的另一部分,它没有客户的概念,也不知道它在哪个客户的集合中运行。在处理它之前,我必须找出该请求需要针对哪个模式

要找到正确的模式,我必须找出R具有特定唯一 ID(字符串)的记录

据我了解,以下

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

只会查看 schema1 中的表(或与表匹配的第一个模式),并且不会进行全局搜索。

有没有办法让我在所有模式中进行全局搜索,还是我只需要使用 for 循环并一次一个地遍历所有模式?

4

3 回答 3

5

您可以为此使用继承。(一定要考虑限制。)

考虑这个小演示:

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

CREATE SEQUENCE master.myseq;  -- global sequence to have globally unique id
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' <-- !!

-- clean it all up:
-- DROP SCHEMA x, y, master CASCADE;

现在,要实际识别特定行所在的表,请使用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;

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

于 2012-10-15T19:13:31.423 回答
1

您将不得不遍历所有命名空间。pg_*您可以从系统目录中获得大量此类信息。理论上,您应该能够在请求时解析客户端 -> 模式映射,而无需与数据库对话,因此您进行的第一个 SQL 调用是:

SET search_path = client1,global_schema;
于 2012-10-15T19:04:39.723 回答
1

虽然我认为 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。如果有多个匹配,结果将是其中之一,但不能保证是哪一个。ORDER BY如果您想按字母顺序或其他方式返回(例如)第一个子句,请在模式查询中添加一个子句。该函数也被简单地修改为返回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);

在这种情况下,它们在创建后完全没有联系。

于 2012-10-16T01:51:18.350 回答