Transforming union to single select with the unions inside from clause - sql

Currently I have UNION's between 3 different select statements giving me three rows. What I'm needing is to modify this to have the union inside the from clause so that I can generate more columns (if I understand the functionality).
Basically what is going on is that the existing framework is designed and built to have a single row of data returned to it and will require heavy modification to handle a multi-row result set (everything is getting parsed to xml before being passed to the front-end).
My biggest issue (I believe) is being able to differentiate between the three sub selects inside the primary.
Guarantees in the select
1) Each select inside the from will only produce a single row result set.
2) All result sets from inside of the from will have same column count and column names (inherent of union I believe)
For example...
I currently have
SELECT * FROM A
UNION
SELECT * FROM B
UNION
SELECT * FROM C
Doing it this way produces a three row result set.
What I'm wanting if possible is....
SELECT cost as CurrentSelectedCost, /* from first select */
cost as PreviousCost, /* from second select */
cost as NextCost /* from third select */
FROM (
SELECT * FROM A
UNION
SELECT * FROM B
UNION
SELECT * FROM C
)
Now I'm guessing that I will need to alias the different select statements that are within the from clause, but I'm having issues getting that to function. The examples that I've found on here didn't seem to address the need to have all select statements inside of from differentiated. If this has been answered on here a link will suffice no reason to re-invent the wheel (I may just not know the terminology to search for) Also the database is a DB2 instance running on an iSeries

You need to unify the columns retrieved from the three SELECT statements.
SELECT CurrentSelectedCost,
PreviousCost,
NextCost
FROM (
SELECT cost as CurrentSelectedCost, 0 as PreviousCost, 0 as NextCost FROM A
UNION
SELECT 0 as CurrentSelectedCost, cost as PreviousCost, 0 as NextCost FROM B
UNION
SELECT 0 as CurrentSelectedCost, 0 as PreviousCost, cost as NextCost FROM C
) as COSTS

SELECT (SELECT COLNAME FROM A) CurrentSelectedCost,
(SELECT COLNAME FROM B) PreviousCost,
(SELECT COLNAME FROM C) NextCost
FROM DUAL

i dont think you need a union. there does not appear to be any join condition so maybe this will do it:
SELECT A.cost as CurrentSelectedCost, /* from first select */
B.cost as PreviousCost, /* from second select */
C.cost as NextCost /* from third select */
FROM A,B,C

Related

SQL to show one result calculated by the other values?

It seems we can use a SQL statement as:
select
(
select
count(*) as c_foos
from
foos
),
(
select
count(*) as c_bars
from
bars
);
but we can't do
select
(
select
count(*) as c_foos
from
foos
),
(
select
count(*) as c_bars
from
bars
),
(
select
(c_foos / c_bars) as the_ratio
);
or
select
(
select
count(*) as c_foos
from
foos
),
(
select
count(*) as c_bars
from
bars
),
(c_foos / c_bars) as the_ratio;
Is there a way to do that showing all 3 numbers? Is there a more definite rule as to what can be done and what can't?
You can try this:
You define two CTEs in a WITH clause, so you can use your result in the main query built on two cte tables (cte_num and cte_den)
WITH recursive
cte_num AS (
SELECT count(*) as c_foos
FROM foos
),
cte_den AS (
SELECT count(*) as c_bars
FROM bars
)
SELECT
cte_num.foos,
cte_den.bars,
cte_num.foos / cte_den.bars as the_ratio
from cte_num, cte_den;
There is a small number of simple rules... but SQL seems so easy that most programmers prefer to cut to the chase, and later complain they didn't get the plot :)
You can think of a query as a description of a flow: columns in a select share inputs (defined in from), but are evaluated "in parallel", without seeing each other. Your complex example boils down to the fact, that you cannot do this:
select 1 as a, 2 as b, a + b;
fields a and b are defined as outputs from the query, but there are no inputs called a and b. All you have to do is modify the query so that a and b are inputs:
select a + b from (select 1 as a, 2 as b) as inputs
And this will work (this is, btw., the solution for your queries).
Addendum:
The confusion comes from the fact, that in most SQL 101 cases outputs are created directly from inputs (data just passes through).
This flow model is useful, because it makes things easier to reason about in more complex cases. Also, we avoid ambiguities and loops. You can think about it in the context of query like: select name as last_name, last_name as name, name || ' ' || last_name from person;
Move the conditions to the FROM clause:
select f.c_foos, b.c_bars, f.c_foos / f.c_bars
from (select count(*) as c_foos from foos
) f cross join
(select count(*) as c_bars from bars
) b;
Ironically, your first version will work in MySQL (see here). I don't actually think this is intentional. I think it is an artifact of their parser -- meaning that it happens to work but might stop working in future versions.
The simplest way is to use a CTE that returns the 2 columns:
with cte as (
select
(select count(*) from foos) as c_foos,
(select count(*) from bars) as c_bars
)
select c_foos, c_bars, (c_foos / c_bars) as the_ratio
from cte
Note that the aliases of the 2 columns must be set outside of each query and not inside (the parentheses).

Efficient way to verify a table is a subset of another table

I have two tables A and B, the structures are exactly the same. I need to verify A is a subset of B. Because the structure contains over 100 fields, I do not want to list them one by one in a where predicates.
I would like to know if there is any more easier way to do that
Assumptions:
(1) Identical table structure of A and B. This means that both order of columns and their data types have to match.
(2) There are no duplicate rows in table A
Problem description
To prove that A is a subset of B you need to show that A\B = empty set.
Solution
This means that if you remove every row in A that has a matching row in B and your output is empty (0 rows) this means that A is subset of B.
If on the other hand, in the output you get > 0 rows it means that A has rows that B doesnt and that A isn't a subset of B.
SELECT * FROM A
EXCEPT
SELECT * FROM B
When A is empty (contains 0 rows) it will be treated as a subset of B, because the result of above query will be 0 rows.
#robertoplancarte's approach with little tweaking
with tB_cnt as
(
SELECT COUNT(*) cnt FROM
(
SELECT DISTINCT * FROM dbo.T_B
) T_B
), TAB_cnt as
(
SELECT count(*) cnt FROM
(
SELECT * FROM dto.T_B
UNION
SELECT * FROM dto.T_A
) T_AB
)
SELECT
CASE WHEN TB_CNT.CNT = TAB_CNT.CNT THEN
'Table A is subset of B'
else
'Table A is not subset of B'
END as Result
FROM TAB_CNT, TB_CNT

SQL Logic: Finding Non-Duplicates with Similar Rows

I'll do my best to summarize what I am having trouble with. I never used much SQL until recently.
Currently I am using SQL Server 2012 at work and have been tasked with trying to find oddities in SQL tables. Specifically, the tables contain similar information regarding servers. Kind of meta, I know. So they each share a column called "DB_NAME". After that, there are no similar columns. So I need to compare Table A and Table B and produce a list of records (servers) where a server is NOT listed in BOTH Table A and B. Additionally, this query is being ran against an exception list. I'm not 100% sure of the logic to best handle this. And while I would love to get something "extremely efficient", I am more-so looking at something that just plain works at the time being.
SELECT *
FROM (SELECT
UPPER(ta.DB_NAME) AS [DB_Name]
FROM
[CMS].[dbo].[TABLE_A] AS ta
UNION
SELECT
UPPER(tb.DB_NAME) AS [DB_Name]
FROM
[CMS].[dbo].[TABLE_B] as tb
) AS SQLresults
WHERE NOT EXISTS (
SELECT *
FROM
[CMS].[dbo].[TABLE_C_EXCEPTIONS] as tc
WHERE
SQLresults.[DB_Name] = tc.DB_NAME)
ORDER BY SQLresults.[DB_Name]
One method uses union all and aggregation:
select ab.*
from ((select upper(name) as name, 'A' as which
from CMS.dbo.TABLE_A
) union all
(select upper(name), 'B' as which
from CMS.dbo.TABLE_B
)
) ab
where not exists (select 1
from CMS.dbo.TABLE_C_EXCEPTION e
where upper(e.name) = ab.name
)
having count(distinct which) <> 2;
SQL Server is case-insensitive by default. I left the upper()s in the query in case your installation is case sensitive.
Here is another option using EXCEPT. I added a group by in each half of the union because it was not clear in your original post if DB_NAME is unique in your tables.
select DatabaseName
from
(
SELECT UPPER(ta.DB_NAME) AS DatabaseName
FROM [CMS].[dbo].[TABLE_A] AS ta
GROUP BY UPPER(ta.DB_NAME)
UNION ALL
SELECT UPPER(tb.DB_NAME) AS DatabaseName
FROM [CMS].[dbo].[TABLE_B] as tb
GROUP BY UPPER(tb.DB_NAME)
) x
group by DatabaseName
having count(*) < 2
EXCEPT
(
select DN_Name
from CMS.dbo.TABLE_C_EXCEPTION
)

SQL "WITH" to include multiple derived tables

Can I write something like below. But this is not giving proper output in WinSQL/Teradata
with
a (x) as ( select 1 ),
b (y) as ( select * from a )
select * from b
Do you really need to use CTEs for this particular solution when derived tables would work as well:
SELECT B.*
FROM (SELECT A.*
FROM (SELECT 1 AS Col1) A
) B;
That being said, I believe multiple CTEs are available in Teradata 14.10 or 15. I believe support for a single CTE and the WITH clause were introduced in Teradata 12 or 13.
You call the dependent 1st and then the parent
like this and it will work. Why is it like that ? Teradata likes people to play with it longer and spend more time with it, making it feel important
with
"b" (y) as ( select * from "a" ),
"a" (x) as ( select '1' )
select * from b

differentiate rows in a union table

I m selecting data from two different tables with no matching columns using this sql query
select * from (SELECT s.shout_id, s.user_id, s.time FROM shouts s
union all
select v.post_id, v.sender_user_id, v.time from void_post v)
as derived_table order by time desc;
Now is there any other way or with this sql statement only can i
differentiate the data from the two tables.
I was thinking of a dummy row that can be created at run-time(in the select statement only ) which would flag the row from the either tables.
As there is no way i can differentiate the shout_id that is thrown in the unioned table is
shout_id from the shout table or from the void_post table.
Thanks
Pradyut
You can just include an extra column in each select (I'd suggest a BIT)
select * from
(SELECT s.shout_id, s.user_id, s.time, 1 AS FromShouts FROM shouts s
union all
select v.post_id, v.sender_user_id, v.time, 0 AS FromShouts from void_post v)
as derived_table order by time desc;
Sure, just add a new field in your select statement called something like source with a different constant value for each source.
SELECT s.shout_id, s.user_id, s.time, 'shouts' as source FROM shouts s
UNION ALL
SELECT v.post_id, v.sender_user_id, v.time, 'void_post' as source FROM void_post v
A dummy variable is a nice way to do it. There isn't much overhead in the grand scheme of things.
p.s., the dummy variable represents a column and not a row.