我们的应用程序使用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'
答案 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列(subdomain
和name
)的表,则它将是这样的:>
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()
您应该会看到结果。
希望这会有所帮助。