根据表值找出哪个模式

时间:2012-10-15 18:50:05

标签: sql django postgresql database-connection postgresql-9.2

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

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

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

据我了解,以下

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

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

我有没有办法在所有模式中进行全局搜索,或者我只需要使用for循环并迭代所有模式,一次一个?

3 个答案:

答案 0 :(得分:5)

您可以使用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;

这也比循环遍历许多单独的表更快

答案 1 :(得分:1)

您必须遍历所有名称空间。您可以从pg_*系统目录中获取大量此类信息。从理论上讲,您应该能够解析客户端 - &gt;请求时的模式映射,而不与数据库通信,因此您进行的第一个SQL调用是:

SET search_path = client1,global_schema;

答案 2 :(得分: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);

在这种情况下,他们在创建后完全没有连接。