Postgresql 9.1 从所有模式中选择

Posted

技术标签:

【中文标题】Postgresql 9.1 从所有模式中选择【英文标题】:Postgresql 9.1 select from all schemas 【发布时间】:2013-12-12 19:13:47 【问题描述】:

我有一个包含数百个模式的 Postgresql 9.1 数据库。都具有相同的结构,只是不同的数据。我需要对表执行选择并从每个模式中获取数据。不幸的是,我还没有找到一个体面的方法来做到这一点。

我尝试将搜索路径设置为 schema_1、schema_2 等,然后对表执行选择,但它只从第一个模式中选择数据。

到目前为止,我设法做到这一点的唯一方法是生成一个大查询,例如:

select * from schema_1.table
union
select * from schema_2.table
union
(...another 100 lines....)

还有其他更合理的方式吗?如果这不可能,我至少可以在不执行此选择的情况下找出哪些模式在该表中有记录吗?

【问题讨论】:

【参考方案1】:

如果您必须了解表中的数据,则必须执行 SELECT。没有其他办法。模式只是逻辑寻址 - 对于您的情况很重要,因此您使用大量表,并且您必须执行大量 UNION。

search_path 按预期工作。它没有任何意义 - 从提到的方案返回数据,但它指定了搜索不完全限定表的顺序。搜索在第一个请求名称的表上结束。

注意:大量联合可能需要大量内存。

您可以对临时表使用动态 SQL 和存储过程:

postgres=# DO $$
   declare r record;
   begin
     drop table if exists result;
     create temp table result as select * from x.a limit 0; -- first table;
     for r in select table_schema, table_name
                  from information_schema.tables
                 where table_name = 'a'
     loop
       raise notice '%', r;
       execute format('insert into result select * from %I.%I',
                                          r.table_schema,
                                          r.table_name);
     end loop;
   end; $$;

结果:

通知:(y,a) 注意:(x,a) 做 postgres=# 从结果中选择 *; 一种 ---- 1 2 3 4 5 ..

【讨论】:

【参考方案2】:

这是一种方法。您需要预先向它提供您所定位的所有架构名称。如果您知道自己想要每个模式,则可以将其更改为仅循环遍历所有模式,如 Pavel 所示。在我的示例中,我关心三个模式,每个模式都包含一个名为 bar 的表。该逻辑将对每个模式的条形表运行选择并将值插入结果表。最后,您有一个表,其中包含所有表中的所有数据。您可以将其更改为更新、删除或执行 DDL。我选择保持简单,只从每个模式中的每个表中收集数据。

--START SETUP AKA Run This Section Once
create table schema3.bar(bar_id   SERIAL PRIMARY KEY,
                         bar_name VARCHAR(50) NOT NULL);

insert into schema1.bar(bar_name) select 'One';
insert into schema2.bar(bar_name) select 'Two';
insert into schema3.bar(bar_name) select 'Three';
--END SETUP

DO $$
   declare r record;
   DECLARE l_id INTEGER = 1;
   DECLARE l_schema_name TEXT;
   begin
     drop table if exists public.result;
     create table public.result (bar_id INTEGER, bar_name TEXT);

     drop table if exists public.schemas;
     create table public.schemas (id serial PRIMARY KEY, schema_name text NOT NULL);
     INSERT INTO public.schemas(schema_name)
     VALUES ('schema1'),('schema2'),('schema3');


     for r in select *
              from public.schemas 
     loop
       raise notice '%', r;

       SELECT schema_name into l_schema_name
       FROM public.schemas
       WHERE id = l_id;

       raise notice '%', l_schema_name;
       EXECUTE 'set search_path TO ' || l_schema_name;

      EXECUTE 'INSERT into public.result(bar_id, bar_name) select bar_id, bar_name from ' || l_schema_name || '.bar';

       l_id = l_id + 1;
     end loop;

   end; $$;


--DEBUG
   select * from schema1.bar;
   select * from schema2.bar;
   select * from schema3.bar;

   select * from public.result;
   select * from public.schemas;

   --CLEANUP
   --DROP TABLE public.result;
   --DROP TABLE public.schemas;

【讨论】:

【参考方案3】:

不同的模式意味着不同的表,所以如果你必须坚持这种结构,那就意味着联合,一种或另一种方式。那可能相当昂贵。如果您在通过搜索路径的便利进行分区之后,反转您的架构可能是有意义的:

在公共架构中存储一个大表,然后在每个单独的架构中提供视图。

查看这个演示我的概念的 sqlfiddle:

http://sqlfiddle.com/#!12/a326d/1

还为后代粘贴内联,以防 sqlfiddle 无法访问:

架构:

CREATE SCHEMA customer_1;
CREATE SCHEMA customer_2;

CREATE TABLE accounts(id serial, name text, value numeric, customer_id int);
CREATE INDEX ON accounts (customer_id);

CREATE VIEW customer_1.accounts AS SELECT id, name, value FROM public.accounts WHERE customer_id = 1;
CREATE VIEW customer_2.accounts AS SELECT id, name, value FROM public.accounts WHERE customer_id = 2;

INSERT INTO accounts(name, value, customer_id) VALUES('foo', 100, 1);
INSERT INTO accounts(name, value, customer_id) VALUES('bar', 100, 1);
INSERT INTO accounts(name, value, customer_id) VALUES('biz', 150, 2);
INSERT INTO accounts(name, value, customer_id) VALUES('baz', 75, 2);

查询:

SELECT SUM(value) FROM public.accounts;

SET search_path TO 'customer_1';
SELECT * FROM accounts;

SET search_path TO 'customer_2';
SELECT * FROM accounts;

结果:

425

1   foo     100
2   bar     100

3   biz     150
4   baz     75

【讨论】:

这似乎不是一个好方法,因为您必须为每个模式手动创建一行。我建议改为创建一个函数。 Plproxy 是 Skype 用来通过具有包装功能的代理数据库在多个数据库分片上运行查询的工具。

以上是关于Postgresql 9.1 从所有模式中选择的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL - 在*所有模式*中授予对所有表(和未来表)的选择

在 Django 中为 ubuntu 11.10 设置 Postgresql 9.1 数据库

从 Laravel 4 中的不同 PostgreSQL 模式中选择表

ubuntu14.04源代码安装postgresql 9.1

如何在 PostgreSQL 9.1 中使用 pgFouine?

从 PostgreSQL 中的字段中提取数字