循环内部的SELECT语句

时间:2019-09-30 19:36:48

标签: sql postgresql

我们的应用程序使用Rails公寓gem,因此每个微型网站都有其自己的架构。因此,我们有50个架构,每个架构都有自己的用户表。

使用SQL(Postgres),如何从所有50个模式中选择用户而不必遍历每个模式?

    DO $$
DECLARE
table_name text;
BEGIN
  FOR schema_name IN SELECT schema FROM tenants LOOP
EXECUTE 'SELECT * FROM ' ||  schema_name || '.users';
  END LOOP;
END;
$$;

结果

subdomain1, 'john smith'
subdomain1, 'mary smith'
subdomain2, 'charles geiger'
subdomain2, 'ann geiger'
subdomain3, 'allison reidy'

3 个答案:

答案 0 :(得分:1)

这是生成所需查询的方式:

WITH relevant_tables AS (
  SELECT CONCAT(nspname, '.', relname) as table_name,
                 CONCAT('SELECT * FROM ', nspname, '.', relname) as table_query
      FROM pg_class c
      LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
      WHERE relkind = 'r'
      AND relname = 'users'
)
SELECT string_agg(table_query, ' UNION ALL ') as final_query FROM relevant_tables

然后可以使用DO块来执行它。

edit considering comments

WITH relevant_tables AS (
  SELECT CONCAT(nspname, '.', relname) as table_name,
                 CONCAT('SELECT * FROM ', nspname, '.', relname) as table_query,
                 nspname as schema
      FROM pg_class c
      LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
      WHERE relkind = 'r'
      AND relname = 'users'
)
SELECT string_agg(table_query, ' UNION ALL ') as final_query
FROM relevant_tables a
LEFT JOIN (SELECT DISTINCT schema FROM tenants) b ON a.schema = b.schmea
WHERE b.schema IS NOT NULL

因此,即使使用DO块,并将结果存储到表(schema.table)中也是如此:

DO
LANGUAGE plpgsql
$$
DECLARE
  stmt text;
BEGIN
    stmt = (
      WITH relevant_tables AS (
    SELECT CONCAT(nspname, '.', relname) as table_name,
           CONCAT('SELECT * FROM ', nspname, '.', relname) as table_query,
           nspname as schema
      FROM pg_class c
      LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
     WHERE relkind = 'r'
       AND relname = 'users'
  )
  SELECT CONCAT('DROP TABLE IF EXISTS schema.table; CREATE TABLE schema.table AS ', string_agg(table_query, ' UNION ALL ')) as final_query
    FROM relevant_tables a
    LEFT JOIN (SELECT DISTINCT schema FROM tenants) b ON a.schema = b.schmea
   WHERE b.schema IS NOT NULL
    );
    EXECUTE stmt;
END;
$$;

答案 1 :(得分:0)

您可能需要UNION运算符,尤其是UNION ALL

基本形式为:

SELECT a,b,c
  FROM schema1.table
UNION ALL
SELECT a,b,c
  FROM schema2.table
UNION ALL
SELECT ...
  ...

基本上,您可以将UNION ALL放在两个不同的SELECT语句之间,查询将在单个结果集中返回两个SELECT的结果。

请注意,这仅在两个SELECT查询返回相同的数据类型集时才有效。所以...

SELECT an_int, another_int, a_text
  ...
UNION ALL
SELECT a_text, another_text, a_timestamp
  ...

将引发错误。

由于您可以将其放在SELECT条语句之间,因此可以连续链接许多SELECT条语句。是的,如果您需要的话,甚至其中的97个。

因为它可能很麻烦,所以通常人们会创建UNION ALL查询的视图并与此相反。在您的情况下,必须为每个新模式更新该视图,因此它可能不可行,但这是一个选择。

哦,差点忘了。 ALL部分的意思是“返回每个查询的所有结果”。如果仅使用UNION而不使用ALL,则数据库将经历重复数据删除过程。例如,如果您在两个不同的模式中具有相同的user,它将仅返回其中一个。 UNION ALL将同时返回两者。

最后,如果您创建了视图,则可以使源代码变得容易一些。像

CREATE VIEW blah AS
  SELECT 'schema1', a,b,c
    FROM schema1.table
  UNION ALL
  SELECT 'schema2', a,b,c
    FROM schema2.table
  UNION ALL
  ...

希望有帮助!

答案 2 :(得分:0)

另一种解决方案,甚至可以让您仅打印结果就可以将其作为查询运行:

首先,运行此命令以创建一个函数,该函数将调用查询(将some_schema更改为可用于存储查询的架构,并将some_function_name更改为要使用的名称)。我不知道结果是什么样子,因为我没有看到您的数据,但是如果它将是一个包含2个varchar列(subdomainname)的表,则它将是这样的:

CREATE OR REPLACE FUNCTION some_schema.some_function_name()
returns TABLE(subdomain varchar(300), name varchar(300))
AS $BODY$
DECLARE
  stmt text;
  BEGIN
    stmt = (
      WITH relevant_tables AS (
        SELECT CONCAT(nspname, '.', relname) as table_name,
        CONCAT('SELECT * FROM ', nspname, '.', relname) as table_query,
        nspname as schema
        FROM pg_class c
        LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
        WHERE relkind = 'r'
        AND relname = 'users'
      )
      SELECT string_agg(table_query, ' UNION ALL ') as final_query
      FROM relevant_tables a
      LEFT JOIN (SELECT DISTINCT schema FROM tenants) b ON a.schema = b.schmea
      WHERE b.schema IS NOT NULL
    );
    return query EXECUTE stmt;
  end; $BODY$
language plpgsql
;

有了此功能,您就可以运行:

select * from some_schema.some_function_name()

您应该会看到结果。

希望这会有所帮助。