根据表值找出哪个模式

Posted

技术标签:

【中文标题】根据表值找出哪个模式【英文标题】:Find out which schema based on table values 【发布时间】:2012-10-05 19:22:55 【问题描述】:

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

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

要找到正确的架构,我必须找出哪个记录​​ R 具有特定的唯一 ID(字符串)

据我了解,如下

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

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

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

【问题讨论】:

据我了解,您想要做的是通过多个模型搜索您输入的字符串?就像假设用户输入“你好”一样,您希望在让我们说模型中搜索这个你好:汽车、公共汽车、自行车。我理解你的问题吗? @Jonathanq:不,每个模式都是相同的,我想在同一个模型中查找数据,只是跨越不同的模式:所以 schema1.cars、schema2.cars、schema3.cars 等。 【参考方案1】:

您可以为此使用inheritance。 (一定要考虑limitations。)

考虑这个小演示:

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;

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

【讨论】:

听起来很复杂。 django 处理 PostgreSQL 继承的能力如何? 我不是 Django 专家,但这一点也不复杂。事实上,如果您在许多模式中都有相同的表,它会大大简化CREATE 脚本。需要明确的是:所有 CREATE 命令都必须只执行一次。但无论如何,这很清楚,对吧?忘记加的8现在加了):如何识别源表。【参考方案2】:

虽然我认为 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);

在这种情况下,它们在创建后彼此完全断开。

【讨论】:

【参考方案3】:

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

SET search_path = client1,global_schema;

【讨论】:

以上是关于根据表值找出哪个模式的主要内容,如果未能解决你的问题,请参考以下文章

我想使用 href 来决定需要打开哪个模式

如何找出哪个进程正在侦听 Windows 上的 TCP 或 UDP 端口?

如何找出哪个进程正在侦听 Windows 上的 TCP 或 UDP 端口?

内联表值 UDF 与视图 - 使用哪个?

如何在分层查询内的相关子查询中传递外部表值

设计模式之工厂模式