Oracle SQL Unions error "query block has incorrect number of results columns" - sql

I'm trying to write a query that pulls data from a lot of tables, and has about 20 unions. It's pulling the same information repeatedly, but with more layers each time, to show a sort of tree.
I want to compare the final two columns. I'm using a case to do this, and if I add a case to this query then I get the error "query block has incorrect number of results columns". This seems to be because the final select in the union has an extra column (the compare case).
Is there any way to work around this? I don't want to add the case to each select, as this would add about 15 more columns that I don't want.

Use a sub-query:
SELECT col1,
col2,
CASE
WHEN col1 = 'somevalue'
THEN 'someresult'
ELSE 'otherresult'
END AS col3
FROM (
SELECT col1, col2 FROM table1 UNION ALL
SELECT col1, col2 FROM table2 UNION ALL
SELECT col1, col2 FROM table3
-- ...
);
Or use a sub-query factoring clause:
WITH data ( col1, col2 ) AS (
SELECT col1, col2 FROM table1 UNION ALL
SELECT col1, col2 FROM table2 UNION ALL
SELECT col1, col2 FROM table3
-- ...
)
SELECT col1,
col2,
CASE
WHEN col1 = 'somevalue'
THEN 'someresult'
ELSE 'otherresult'
END AS col3
FROM data;

Related

SQL UNION - Adding Source

I am currently using UNION on two queries (see psuedo-code below):
query1
UNION
query2
I want to add an additional column to my results that says the source of the data. The new column called "Source" would return one of the following: "1", "2", or "both".
Being able to handle "both" is very important because query1 and query2 will have similar results and many overlapping records. If anyone could help point me in the right direction, especially with how to handle the "both" case, that would be greatly appreciated!
Sample:
If query1 has a row "Apple,Yellow,Bob" and query2 has the same row, then the result I'm hoping for is:
"Apple,Yellow,Bob,Both"
The individual queries themselves will not have duplicates, but there may be the same row both in query1 and query2 (as seen above).
you can make use of an additional column col4 like this
select col1,col2,col3,sum(col4)
from(
Select col1, col2, col3, 1 as col4 from table1
UNION
Select col1,col2,col3, 2 as col4 from table4
)
group by col1,col2,col3
The records with col4=1 only exist in table1.
The records with col4=2 only exist in table2.
The records with col4=3 exist in both table1+table
add a Source field to both query 1 and query 2:
select 1 as source, ...
from table1
union
select 2 as source, ...
from table2
Here's one way
WITH T
AS (SELECT '1' AS Source,
Col1,
Col2,
Col3
FROM table1
UNION ALL
SELECT '2' AS Source,
Col1,
Col2,
Col3
FROM table2)
SELECT CASE
WHEN MAX(Source) = MIN(Source) THEN Source
ELSE 'Both'
END AS Source,
Col1,
Col2,
Col3
FROM T
GROUP BY Col1,
Col2,
Col3
One more approach
SELECT col1
,col2
,source = CASE
WHEN count(DISTINCT source) > 1
THEN 'Both'
ELSE max(source)
END
FROM (
SELECT col1 ,col2, source = 'source1'
FROM source1
UNION ALL
SELECT col1, col2, source = 'source2'
FROM source2
) u
GROUP BY col1, col2
You can try this
SELECT
a.col1 , a.col2,
CASE WHEN MAX(a.Source) <> MIN(a.Source)
THEN 'BOTH'
ELSE MAX(a.Source) END
FROM
(
SELECT
col1, col2 ,'Source2' AS Source
FROM Table1
UNION ALL
SELECT
col1, col2 ,'Source1' AS Source
FROM Table2
) a
GROUP BY
a.col1 , a.col2
Link to the Sample

Concatenate tables (UNION ALL) where one of the tables lacks one of the columns

I am trying to combine three tables in an SQLite database into one new combined table. The three tables have the same column names, but the third table is missing one of the columns. Here is how I am trying to do it:
CREATE TABLE cobmined
AS
SELECT col1, col2, col3
FROM
(
SELECT col1, col2, col3 from table1
UNION ALL
SELECT col1, col2, col3 from table2
UNION ALL
SELECT col1, col2 from table3
) s
;
This works when doing this only on the first two tables, when adding the third table I get the message:
SELECTs to the left and right of UNION do not have the same number of result columns
Is there a way to let SQL ignore the missing column and leave it with NULLs if needed?
Add a NULL value to the third table
CREATE TABLE cobmined
AS
SELECT col1, col2, col3
FROM
(
SELECT col1, col2, col3 from table1
UNION ALL
SELECT col1, col2, col3 from table2
UNION ALL
SELECT col1, col2, null from table3
) s
;
Also, no need for sub-query
CREATE TABLE cobmined
AS
SELECT col1, col2, col3 from table1
UNION ALL
SELECT col1, col2, col3 from table2
UNION ALL
SELECT col1, col2, null from table3
I want to note that you don't need the subquery:
CREATE TABLE combined AS
SELECT col1, col2, col3 from table1
UNION ALL
SELECT col1, col2, col3 from table2
UNION ALL
SELECT col1, col2, NULL from table3;
In addition, you may find that a view is more suitable for your purposes than an actual table.

SQL - Delete repeated rows in table

I need to delete the repeated row-
I have this table-
source
The result that I need-
result
*keep only one combination of 2 column (the order is not important)
Thanks! (:
Here is one method that should be efficient:
select col1, col2
from t
where col1 <= col2
union all
select col1, col2
from t
where col1 > col2 and
not exists (select 1 from t t2 where t2.col1 = t.col2 and t2.col2 = t.col1);
Note: This is a SQL select statement, so it does not delete rows in the table. You seem to want the results from a query, not to modify the underlying table.
My interpretation of the spec "keep only one combination of 2 column, [column] order not important":
SELECT col1, col2
FROM t
WHERE col1 <= col2
UNION
SELECT col2, col1
FROM t
WHERE col1 > col2;

SQL Where Not Exists

I think I have a misunderstanding of how NOT EXISTS work and hope it can be clarified to me.
Here is the sample code I am running (also on SQL Fiddle)
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
)
where not exists(
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
I thought that it should return:
1, 1, 1
But instead it returns nothing.
I make this assumption only on the fact that I though NOT EXISTS would give me a list of all the rows in the first query that do not exist in the second query (in this case 1,1,1)
Why does this not work
What would be the appropriate way to make it work the way I am expecting it to?
You are performing an uncorrelated subquery in your NOT EXISTS() condition. It always returns exactly one row, therefore the NOT EXISTS condition is never satisfied, and your query returns zero rows.
Oracle has a rowset difference operator, MINUS, that should do what you wanted:
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
MINUS
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
SQL Server has an EXCEPT operator that does the same thing as Oracle's MINUS. Some other databases implement one or the other of these.
EXISTS just returns true if a record exists in the result set; it does not do any value checking. Since the sub-query returns one record, EXISTS is true, NOT EXISTS is false, and you get no records in your result.
Typically you have a WHERE cluase in the sub-query to compare values to the outer query.
One way to accomplish what you want is to use EXCEPT:
select sum(col1) col1, sum(col2) col1, sum(col3) col3
from (
select 1 col1, 1 col2, 1 col3
from dual tbl1
)
EXCEPT(
select 2 col1, 1 col2, 1 col3
from dual tbl2
)
A not exists that includes a select from dual will never return anything. Not exists will exclude rows where the embedded SQL returns something. Normally not exists should be used more like this:
select ... from MY_TABLE A where not exists (select 1 from OTHER_TABLE B where A.SOME_COL = B.SOME_COL)
As using NOT EXISTS is not good approach as it is return only single row so try it with MINUS or EXCEPT
select sum(col1) col1, sum(col2) col1, sum(col3) col3 from ( select 1 col1, 1 col2, 1 col3 from dual tbl1 MINUS select 2 col1, 1 col2, 1 col3 from dual tbl2 )
select sum(col1) col1, sum(col2) col1, sum(col3) col3 from ( select 1 col1, 1 col2, 1 col3 from dual tbl1 ) EXCEPT( select 2 col1, 1 col2, 1 col3 from dual tbl2 )

merge two queries with different where and different grouping into 1

Sorry, I asked this question just before and got some good answers but then I realised I made a mistake with the query in question, if I change the question in the original post that could make the answers invalid so I'm posting again with the right query this time, please forgive me, I hope this is acceptable.
DECLARE #Temp TABLE
(MeasureDate, col1, col2, type)
INSERT INTO #Temp
SELECT MeasureDate, col1, col2, 1
FROM Table1
WHERE Col3 = 1
INSERT INTO #Temp
SELECT MeasureDate, col1, col2, 3
FROM Table1
WHERE Col3 = 1
AND Col4 = 7000
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM #Temp
GROUP BY MeasureDate, Type
I do two inserts into the temp table, 2nd insert with an extra WHERE but same columns same table, but different type, then I do SUM(col1) / SUM(col2) on the temp table to return the result I need per MeasureDate and type. Is there a way to merge all these inserts and selects into one statement so I don't use a temp table and do a single select from Table1? Or even if I still need the temp table, merge the selects into one select instead of two separate selects? Stored procedure works fine as it is, just looking for a way to shorten it.
Thanks.
Sure can. I might start with combining the two queries from your inserts using UNION ALL (this variation of UNION will not remove duplicates), wrapped up in a CTE from which you can perform your final query:
WITH MeasureData(MeasureDate, col1, col2, type) AS (
SELECT MeasureDate, col1, col2, 1
FROM Table1
WHERE Col3 = 1
UNION ALL
SELECT MeasureDate, col1, col2, 3
FROM Table1
WHERE Col3 = 1
AND Col4 = 7000
)
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM MeasureData
GROUP BY MeasureDate, Type
That's it, no more table variable or insert statements.
No real need for a UNION, you can handle this with a CASE statement:
SELECT SUM(col1) / SUM(col2) AS Percentage, MeasureDate, Type
FROM (
SELECT MeasureDate, col1, col2, case when Col4 = 7000 then 3 else 1 end type
FROM Table1
WHERE Col3 = 1
) t
GROUP BY MeasureDate, Type
Edit, as Gordon correctly points out, for Type = 1, this query wouldn't produce the same results. Here's a variation on Gordon's good answer that might be easier to visually understand using a CROSS JOIN and IF logic:
SELECT T1.MeasureDate,
T.Type,
SUM(IF(T.Type=1,Col1,IF(T.Type=3 AND T1.Col4=7000,T1.Col1,0))) /
SUM(IF(T.Type=1,Col2,IF(T.Type=3 AND T1.Col4=7000,T1.Col2,0))) AS Percentage
FROM Table1 T1
CROSS JOIN (SELECT 1 Type UNION SELECT 3) T
WHERE T1.Col3 = 1
GROUP BY T1.MeasureDate, T.Type
Condensed SQL Fiddle
Your method is double counting cases where col3 = 1 and col4 = 7000. Here is a method that takes this into account, without union on the overall table:
select t.type, SUM(t1.col1) / SUM(t1.col2) AS Percentage, t1.MeasureDate, t.Type
from table1 t1 join
(select 1 as type union all
select 3 as type
) t
on t.type = 1 or t1.col4 = 7000
where t1.col3 = 1
group by measuredate, type;