How to UNION a list of tables retrieved from another table with a single query? - sql

I have a table with a list of tables in PostgreSQL:
|id|table |
|--|------|
|1 |table1|
|2 |table2|
|3 |table3|
I want to select from a union of all these tables like (pseudo-code):
select * from union(select table from tablenames)

To automate this, you need dynamic SQL
CREATE OR REPLACE FUNCTION f_multi_select()
RETURNS SETOF table1
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
(
SELECT string_agg(format('SELECT * FROM %I', tbl), ' UNION ALL ')
FROM (SELECT tbl FROM tablenames ORDER BY id) sub
);
END
$func$;
Call:
SELECT * FROM f_multi_select();
Assuming that all tables share the same row type - so we can pick any to define the return type of the function.
I threw in a subquery with ORDER BY - in case the order of tables is meaningful.
Related:
Return SETOF rows from PostgreSQL function
Table name as a PostgreSQL function parameter

Here is one way you can do this without using dynamic SQL. Let's say that you only had 10 possible tables in your schema. Then, you could write the following query:
select * from table1 where 'table1' in (select "table" from tablenames) union all
select * from table2 where 'table2' in (select "table" from tablenames) union all
select * from table3 where 'table3' in (select "table" from tablenames) union all
...
select * from table10 where 'table10' in (select "table" from tablenames);
The drawback of this approach is that it requires hard coding a query for each possible table.
I also assume here that select * makes sense, because each of the ten tables would have the same number and types of columns.

Related

How to add union data in table?

Thanks in advance !!
I want to get below data in separate table with column how can we achieved this.
From my reading of your question, you would like the results of that SELECT statement put into a new table?
Firstly, I'm assuming your original SQL works as a SELECT statement - e.g., all those tables have the same structure. Note that you can simplify the unions, but I haven't done so here, to keep the key part of the answer (saving the data) as the main focus.
To save the data into another table, you can either create a table first and make that into an insert, or just use 'SELECT INTO' within the main SELECT.
If you are happy with the columns being automatically created, the 'SELECT INTO' version will create columns (e.g., you do not need to specify the columns in a CREATE TABLE statement). However, when you run the SELECT INTO, it does create the table. Therefore if you want to insert further values, you need to specify the column list (or have matching column lists).
SELECT INTO version
select *
INTO #Temp -- Added This row
from
( select * from #OneyearExpiry
union all
select * from #OtherYearExpiry
) A
except
select * from
( select * from #ONEYRCON
union all
select * from #OTHERYRCON
) B
INSERT INTO version
CREATE TABLE #Temp (<your fields here to match the SELECT statement>)
INSERT INTO #Temp
select * from
( select * from #OneyearExpiry
union all
select * from #OtherYearExpiry
) A
except
select * from
( select * from #ONEYRCON
union all
select * from #OTHERYRCON
) B
Set operators are evaluated from top to bottom so there only needs to be 1 subquery. Something like this
select ab.* into #Temp
from (select * from #OneyearExpiry
union all
select * from #OtherYearExpiry
except
select * from #ONEYRCON
except
select * from #OTHERYRCON) ab;

Is there a SQL function to expand table?

I vaguely remember there being a function that does this, but I think I may be going crazy.
Say I have a datatable, call it table1. It has three columns: column1, column2, column3. The query
SELECT * FROM table1
returns all rows/columns from table1. Isn't there some type of EXPAND function that allows me to duplicate that result? For example, if I want to duplicate everything from the SELECT * FROM table1 query three times, I can do something like EXPAND(3) ?
In BigQuery, I would recommend a CROSS JOIN:
SELECT t1.*
FROM table1 CROSS JOIN
(SELECT 1 as n UNION ALL SELECT 2 UNION ALL SELECT 3) n;
This can get cumbersome for lots of copies, but you can simplify this by generating the numbers:
SELECT t1.*
FROM table1 CROSS JOIN
UNNEST(GENERATE_ARRAY(1, 3)) n
This creates an array with three elements and unnests it into rows.
In both these cases, you can include n in the SELECT to distinguish the copies.
Below is for BigQuery Standard SQL
I think below is close enough to what "got you crazy" o)
#standardSQL
SELECT copy.*
FROM `project.dataset.tabel1` t, UNNEST(FN.EXPAND(t, 3)) copy
To be able to do so, you can leverage recently announced support for persistent standard SQL UDFs, namely - you need to create FN.EXPAND() function as in below example (note: you need to have FN dataset in your project - or use existing dataset in which case you should use YOUR_DATASET.EXPAND() reference
#standardSQL
CREATE FUNCTION FN.EXPAND(s ANY TYPE, dups INT64) AS (
ARRAY (
SELECT s FROM UNNEST(GENERATE_ARRAY(1, dups))
)
);
Finally, if you don't want to create persistent UDF - you can use temp UDF as in below example
#standardSQL
CREATE TEMP FUNCTION EXPAND(s ANY TYPE, dups INT64) AS ( ARRAY(
SELECT s FROM UNNEST(GENERATE_ARRAY(1, dups))
));
SELECT copy.*
FROM `project.dataset.tabel1` t, UNNEST(EXPAND(t, 3)) copy
if you want a cartesian product (all the combination on a row ) you could use
SELECT a.*, b.*, c.*
FROM table1 a
CROSS JOIN table1 b
CROSS JOIN table1 c
if you want the same rows repeated you can use UNION ALL
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table1
Use union all
Select * from table1
Union all
Select * from table1
Union all
Select * from table1
Union all
Select * from table1
For reuse purposes can embed this code in a procedure like
Create Procedure
expandTable(tablename
varchar2(50))
As
Select * from table1
Union all
Select * from table1
Union all
Select * from table1
Union all
Select * from table1
End
/

Two SQL statements, ignore the return of the first if it has no results?

I have a sql statement SELECT * FROM table1 WHERE ....; SELECT * FROM table2 WHERE ....
What I want is to get the results from the first select statement if it returns results, but if it doesn't, I want to ignore it and just get the results from the second select statement. Is there a way I can do this just using SQL?
I'm getting this returned to me as a datatable, using a dataadapter to fill the datatable from the above SQL statement. I can't change that part, or switch to filling a dataset (for reasons I won't get into, but it just can't be changed).
Assuming both queries return the same number and type of columns, one way to do this would be:
select * from table1 where ... /* query 1 conditions */
union all
select * from table2 where ... /* query 2 conditions */
and not exists
(select 1 from table1 where ... /* query 1 conditions */)
A couple options. You can check the count first:
If (select count(*) from table1 where...) > 0
begin
select * from table1 where...
end
else
begin
select * from table2 where...
end;
if both result sets are identical in structure, you can save the count check (and thus improve performance) by using a temp table:
create table #temp (col1 int, col2 varchar(10), ...);
insert #temp
select * from table1 where...;
if ##rowcount = 0
begin
insert #temp
select * from table2 where...
end;
select * from #temp;

Reuse select query in a procedure in Oracle

How would I store the result of a select statement so I can reuse the results with an in clause for other queries? Here's some pseudo code:
declare
ids <type?>;
begin
ids := select id from table_with_ids;
select * from table1 where id in (ids);
select * from table2 where id in (ids);
end;
... or will the optimizer do this for me if I simply put the sub-query in both select statements?
EDIT: Here's more information about the structure of my tables.
Basically table1 is a standard table with the id being the primary key. While table2 has a 3-column primary key with id being one of those columns. In my case the id in table2 will appear in three rows.
You could use a SQL table object to store the result of the select and reuse it. It will consume more memory and will probably be efficient only if the first SELECT takes a lot of time.
CREATE TYPE tab_number IS TABLE OF NUMBER;
/
You would use it with a BULK COLLECT INTO clause:
DECLARE
ids tab_number;
BEGIN
SELECT id BULK COLLECT INTO ids FROM table_with_ids;
SELECT * /*into ??*/
FROM table1
WHERE id IN (SELECT column_value FROM TABLE(ids));
SELECT * /*into ??*/
FROM table2
WHERE id IN (SELECT column_value FROM TABLE(ids));
END;
In version 9i and before you would need to use CAST to query the table:
SELECT *
FROM table2
WHERE id IN (SELECT column_value FROM CAST (TABLE(ids) AS tab_number));
Alternatively, you could use a GLOBAL TEMPORARY TABLE to store the intermediate result set.

SQL conditional union

Question: I have an SQL function which returns a list of files
now I should join an additional list to that list with an union, but only if the user is admin.
Is that possible? Something like:
CREATE FUNCTION tfu_CMS_Process(#bIsAdmin bit )
-- Add the parameters for the function here
RETURNS TABLE
AS
RETURN
(
SELECT * FROM TABLE1
if bIsAdmin
UNION ALL
SELECT * FROM TABLE2
end if
)
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table2
WHERE #isAdmin = 1