JTa
JTa

Reputation: 341

Loop through and combine schemas/tables with UNION_ALL

I want to combine tables from different schemas using UNION_ALL. The tables have the same schema, like in this toy example:

class1.names
+----------+
| id | name|
+----------+
| 1  | jon |
| 2  | ann |
| 3  | rob |

class2.names
+----------+
| id | name|
+----------+
| 1  | rav |
| 2  | meg |
| 3  | mat |

I can hardcode the list of classes into an array, or more preferably, obtain them using a query like so:

SELECT DISTINCT(TABLE_SCHEMA)
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE = 'BASE TABLE'

I want to combine the tables like so:

SELECT *, 'class1' as class FROM class1.names
UNION_ALL
SELECT *, 'class2' as class FROM class2.names
UNION_ALL
etc.

But there will be a lot more going on in the schema-level queries than SELECT *, 'class1'... so I want to do this using a loop or some other systematic method.

I'm looking at dynamic sql or using GROUP_CONCAT with 'UNION_ALL' as a separator, but I'm having trouble making progress.

Addendum: I know this is poor schema design but I can't do anything about that right now.

Upvotes: 3

Views: 3867

Answers (3)

waldente
waldente

Reputation: 1434

In Snowflake, this dynamic SQL :

with a as (
  select * from information_schema.tables 
  where table_catalog like 'CLASS%' and table_name = 'NAMES' and table_type = 'BASE TABLE'
),

b as (
  select *, 
    $$SELECT *, 'SCHEMA' as class FROM SCHEMA.names$$ as t,
    replace(t,'SCHEMA',lower(table_schema)) as sql,
  from a
)

select listagg(sql,'\nUNION ALL\n') within group (order by table_schema, table_catalog)
from b;

will produce :

SELECT *, 'class1' as class FROM class1.names
UNION ALL
SELECT *, 'class2' as class FROM class2.names
UNION ALL
etc.

The $$...$$ are an alternative to single quotes for string literals. You can also escape single quotes by doubling them up.

Upvotes: 1

Keith
Keith

Reputation: 11

Maybe something like:

SELECT DISTINCT case when row_number() OVER (ORDER BY lower(table_schema) || '.' || lower(table_name)) > 1 then 'UNION ALL ' else '' end ||
                   'SELECT *, ''' || lower(table_schema) || ''' AS class FROM ' || lower(table_schema) || '.' || lower(table_name) || '' union_stmt
FROM INFORMATION_SCHEMA.TABLES 
WHERE table_type = 'BASE TABLE'
and table_name like 'HPSA%';

Upvotes: 0

Andrii Soldatenko
Andrii Soldatenko

Reputation: 567

If i understand your correctly:

select listagg('select ' || f.value, ' union all ') from table(flatten(input => parse_json(
                                                    '[1, ,77]'))) f;
+----------------------------------------------+
| LISTAGG('SELECT ' || F.VALUE, ' UNION ALL ') |
|----------------------------------------------|
| select 1 union all select 77                 |
+----------------------------------------------+
1 Row(s) produced. Time Elapsed: 0.739s

and than:

select 1 union all select 77;
+----+
|  1 |
|----|
|  1 |
| 77 |
+----+
2 Row(s) produced. Time Elapsed: 0.638s

Upvotes: 0

Related Questions