How to find minimum from a set of minimums in a query? - sql

I have a query which looks something like :
Select min(x) from A
UNION
Select min(x) from B
UNION
Select min(x) from C
.
.
.
Select min(x) from Z
Now , i want to return the minimum of these values using a SELECT statement.
Is it possible , considering i'm using DB2 as my database?
P.S. : I'm aware that i can insert the output of the above in temp table and get the min from there. But the Insert operation is costly , since most of these have > 1BN rows.
Thanks

You could do it using a nested SELECT:
SELECT MIN(V) FROM (
Select min(x) AS V from A
UNION
Select min(x) AS V from B
UNION
Select min(x) AS V from C
.
.
.
Select min(x) AS V from Z
) t
Since you are applying an aggregate function to the values, you may as well replace UNION with UNION ALL.

Related

Multiple Selects with Union disordered?

I have multiple selects joined by a Union as following
(
select * from x
)
UNION
(
select * from y
)
UNION
(
select * from z
)
But I'm getting disordered results on the SSMS:
z results
x results
y results
Is it possible to force results to be x, y, z?
You must provide an ORDER BY clause, otherwise no ordering is guaranteed. For example, the rows from different tables could even be interspersed.
A good option for explicit ordering, is to add a column and order by it
select *, 1 as ordering
from x
UNION
select *, 2
from y
UNION
select *, 3
from z
order by ordering;
I suspect you actually don't need UNION here (which implies DISTNCT) and you really want UNION ALL.

Use row value from CTE in function

I'm trying to use row values from CTE expressions to pass to a function.
Something like:
WITH
start_number(x) as (VALUES(1)),
end_number(y) as (VALUES(10)),
z AS (SELECT * FROM generate_series(start_number.x, end_number.y))
SELECT z.*
What's the syntax?
Notes: start_number & end_number may be the result of a query, typically returning a single row.
You need to reference the two CTEs in the FROM clause:
WITH start_number(x) as (
VALUES(1)
), end_number(y) as (
VALUES(10)
)
SELECT *
FROM start_number, end_number, generate_series(start_number.x, end_number.y))
You can simplify that by using only a single CTE for the numbers:
WITH params(start_number, end_number) as (
VALUES (1, 10)
)
SELECT *
FROM params p, generate_series(p.start_number, p.end_number))
You need a from clause so you can get x and y:
WITH start_number(x) as (
VALUES (1)
),
end_number(y) as (
VALUES (10)
),
z AS (
SELECT gs.z
FROM start_number CROSS JOIN
end_number CROSS JOIN LATERAL
generate_series(start_number.x, end_number.y) gs(z)
)
SELECT z.*
FROM z;
Merely defining a CTE does not make it available to subsequent code and CTEs in the query. You need to include the CTE in a FROM clause.
In addition, you should be in the habit of naming the columns in CTEs. In your code z has a column with no name.
The above is one method. You could also use subqueries as well:
WITH start_number(x) as (
VALUES(1)
),
end_number(y) as (
VALUES(10)
),
z AS (
SELECT *
FROM generate_series((SELECT x FROM start_number), (SELECT y FROM end_number))
)
SELECT z.*
FROM z;
Here is a db<>fiddle.

SQL statement to return non-intersection records

I was recently asked this question and was a little stumped so I want to ask the experts...
Given two tables A & B, I want to return all the values from A and B that do not overlap. Think of two overlapping circles; how do we return all the data that is NOT in the overlapping center section? And, I had to use ANSI Standard SQL rather than Oracle syntax.
Assuming we want everything exclusive to both A & B, my answer was
select *
from A
cross join B
minus
(select a.common_column from a
intersect
select b.common_column)
Does this look correct, or even close? If it is correct, is there a more efficient way to do this?
BTW - my solution was soundly rejected....
Thank you!
Given the tables A and B, you are looking for (A U B) - (A & B). In other words, you need A union B minus their intersection. Remember A and B must be union-compatible for this query to work. I would do:
(select * from A
union
select * from B
)
minus
(select * from A
intersect
select * from B
)
May be full outer join?
select coalesce(A.col, B.col)
from A full outer join B on A.col = B.col
where A.col is null or B.col is null;
For computing a set symmetric difference, you can use a combination of MINUS and UNION ALL:
select * from (
(select * from A
minus
select * from B)
union all
(select * from B
minus
select * from A)
)
Your query was rejected because it is syntactically incorrect: the number of columns differ and it confuses cross join and union all. However, I think you have the right idea for solving this.
You can easily fix this:
(select *
from A
union all
select *
from B
) minus
(select *
from A
intersect
select *
from B
);
That is, combine everything using union all and then subtract the rows that occur in both tables.
Of course, if there is a single id, then you can use the id with join and other operations.
Just like Frank Schmitt answered in the meantime:
Here it is including a data example:
WITH
table_a(name) AS (
SELECT 'From_A_1'
UNION ALL SELECT 'From_A_2'
UNION ALL SELECT 'From_A_3'
UNION ALL SELECT 'From_A_4'
UNION ALL SELECT 'From_A_5'
UNION ALL SELECT 'From_BOTH_6'
UNION ALL SELECT 'From_BOTH_7'
UNION ALL SELECT 'From_BOTH_8'
)
,
table_b(name) AS (
SELECT 'From_B_1'
UNION ALL SELECT 'From_B_2'
UNION ALL SELECT 'From_B_3'
UNION ALL SELECT 'From_B_4'
UNION ALL SELECT 'From_B_5'
UNION ALL SELECT 'From_BOTH_6'
UNION ALL SELECT 'From_BOTH_7'
UNION ALL SELECT 'From_BOTH_8'
)
(SELECT * FROM table_a EXCEPT SELECT * FROM table_b)
UNION ALL
(SELECT * FROM table_b EXCEPT SELECT * FROM table_a)
ORDER BY name
;
name
From_A_1
From_A_2
From_A_3
From_A_4
From_A_5
From_B_1
From_B_2
From_B_3
From_B_4
From_B_5
You will need to select all the data from both tables, except where they overlap, and then combine the data with a union. The code provided should work for your example.
SELECT *
FROM
(
SELECT * FROM Table1
EXCEPT SELECT * FROM Table2
)
UNION
SELECT *
FROM
(
SELECT * FROM Table2
EXCEPT SELECT * FROM Table1
)
Hope this helps.

Select from a subset of data

I am working on a query in MS SQL Server 2014
That basically is a lot of unions
select x, y
where a = b
union
select x, y
where a = b
union
select x, y
where a = b
It works fine, however the where clauses are identical on every select. And for ease of maintenance I was wondering if there is a better, cleaner way to do this.
I was thinking of somehow selecting the data with the where clauses first then doing all the other queries only on this data.
But im open to any ideas on how to improve this query.
;WITH Test AS
(
SELECT x, y
UNION
SELECT x, y
UNION
SELECT x, y
)
SELECT * FROM Test
WHERE a = b
You could use a sub query and take the where clause outside of it for ease of maintenance.
Just make sure you bring all the columns through in the sub query that you will need in the where clause. Eg
SELECT * FROM
(
SELECT x,y,a,b FROM table1
union
SELECT x,y,a,b FROM table2
UNION
SELECT x,y,a,b FROM table3
)subquery
WHERE a=b
Select *
From
(
select x, y
union
select x, y
union
select x, y
) MyDerivedTable
Where ...
Make sure to include the columns you need to filter in the select statement of the tables inside the derived table.

Flattening nested record in postgres

How to flatten the foo column in the outer select (in PostgreSQL)?
WITH RECURSIVE t AS (
SELECT row(d.*) as foo FROM some_multicolumn_table as d
UNION ALL
SELECT foo FROM t WHERE random() < .5
)
SELECT foo FROM t
What I want is to output all the columns (horizontally, i.e. as a row of multiple columns) of some_multicolumn_table in the outer select, not just a single "record" column.
How to do that?
You don't need the ROW constructor there, and so you can expand the record by using (foo).*:
WITH RECURSIVE t AS (
SELECT d as foo FROM some_multicolumn_table as d
UNION ALL
SELECT foo FROM t WHERE random() < .5
)
SELECT (foo).* FROM t;
Although this query could be simple written as:
WITH RECURSIVE t AS (
SELECT d.* FROM some_multicolumn_table as d
UNION ALL
SELECT t.* FROM t WHERE random() < .5
)
SELECT * FROM t;
And I recommend trying to keep it as simple as possible. But I'm assuming it was just an exemplification.