Union and union all in oracle Database - sql

While executing the below two queries (focus on the part between two asterisks * ____ *), I am really wondering how does the position of UNION ALL changes the output. I am unable to understand.
Query 1
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
*UNION All SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL*
UNION SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
Query Result
NAME MARKS
Jack 100
Query 2
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
UNION SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
*UNION ALL SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL*
Query Result
NAME MARKS
Jack 100
Jack 100
Thanks :)

If you don't specify parantheses, the selects would get executed one after the other. All set operators minus, union, union all, intersect have the same precedence.
Oracle Documentation
In the first query, UNION is performed at the end so there would be no dup rows per the query. In the second, UNION ALL is performed at the end, so there would be dup rows per your query.

The difference between Union and Union all is that Union all will not eliminate duplicate rows,
first query output:
step 1:
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
UNION All SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
result 2 rows because union all allows duplicates.
step 2:
UNION SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
this query will pick only row without duplicates from the above 2 rows and itself.
returns 1 row.
in the second query...
step 1
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
UNION SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
returns 1 row because union selects only distinct rows.
step 2
UNION ALL SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
return 2 rows because it allows duplicates.

Both union and union all have the same precedence as operations. So in the absence of parentheses, your two unions would be evaluated from top to bottom. Your first query is being evaluated like this:
SELECT Name, Marks
FROM
(
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
UNION All
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
) t
UNION
SELECT 'Jack' AS Name, 100 AS Marks FROM DUAL
The same reasoning applies to your second query.

To understand this behavior,you need to be clear with the difference between UNION and UNION ALL.
Query 1: Output of first two lines will return 2 rows. However, last line(sql statement) with 'UNION' operator will remove duplicate rows,including it's own output. So, total first query will return only 1 row.
Query 2: Now, UNION is on 2nd line. After getting combined with 1st line, it will again return only 1 row. But, on the 3rd line with 'UNION ALL',it will return 2 rows. Because, 'UNION ALL' won't remove duplicates.

Related

oracle query to show tourist_name' and the places which is not visited by tourist

I have one table with two columns in it. First one is 'tourist_name' and second one is 'visited_places'. So I need a query which will give me following output :
'tourist_name' and the places which is not visited by tourist.
You can generate the rows using cross-join and then simply use minus clause to get your desired result -
WITH TAB AS (SELECT 1 TOURIST_NAME, 'B' VISITED_PLACE FROM DUAL UNION ALL
SELECT 1, 'C' FROM DUAL UNION ALL
SELECT 1, 'D' FROM DUAL UNION ALL
SELECT 2, 'A' FROM DUAL UNION ALL
SELECT 2, 'E' FROM DUAL)
SELECT TOURIST_NAME, VISITED_PLACE FROM (SELECT DISTINCT VISITED_PLACE FROM TAB),
(SELECT DISTINCT TOURIST_NAME FROM TAB)
MINUS
SELECT TOURIST_NAME, VISITED_PLACE FROM TAB;
Link.

How can I add two columns sequentially (and not concatenate)?

I am trying to pull two tables from an Oracle SQL database, and want to join them sequentially, so they appear as if they are one list.
List one has items [1,2,3,4]
List two has items [a,b,c,d]
I want to output [1,2,3,4,a,b,c,d]
Any thoughts?
One option uses a UNION with a computed column:
SELECT val
FROM
(
SELECT val, 1 AS position FROM table1
UNION ALL
SELECT val, 2 AS position FROM table2
) t
ORDER BY
position, val;
Demo
Note that I assume that all data here is text. If not, e.g. the first table be numeric, then we would have to do a cast along the way. But, this is not the main focus of your question anyway.
SELECT id_1, value_column1 from table_1
UNION
SELECT id_2, value_column2 from table_2;
if the types of columns are different - make sure you cast/convert them to char() - the resulting type should be same.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries004.htm
use union, i think 1,2,3 as numeric value that why converted it on varchar as for union you have to same data type
with t1 as (
select 1 as id from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual
), t2 as (
select 'a' as item from dual union all
select 'b' from dual union all
select 'c' from dual union all
select 'd' from dual
)
select cast(id as varchar(20)) as id from t1
union
select * from t2
demo
output
1
2
3
4
a
b
c
d

query with LEAST returning multiple rows in oracle

When I ran the below query in Oracle 11g
SELECT least(val)
FROM
(
SELECT 1 AS val
FROM dual
UNION
SELECT 2 AS val
FROM dual
UNION
SELECT 3 AS val
FROM dual
);
I was expecting a single row but it is returning multiple rows. please help me out where exactly my understanding is going wrong..
Oracle's LEAST function returns the least value in a list of expressions, e.g. LEAST(1, 2, 3) would return 1. So LEAST could be used to find the minimum value across a collection of columns, e.g. LEAST(col1, col2, col3). What you are seeing is to be expected, i.e. you are getting back three records with the smallest value of each record.
Instead, if you want the minimum over an aggregate of rows, then you should be using MIN, e.g.
select min(val)
from
(
select 1 as val from dual union all
select 2 from dual union all
select 3 from dual
);

Oracle Get sum of distinct group without subquery

I already have a working example which does exactly what I need.
Now the problem is, that I'm not really a fan of subqueries and I think there could be a better solution to this problem.
So here is my (already) working example:
with t as
(
select 'Group1' as maingroup,'Name 1' as subgroup, 'random' as random, 500 as subgroupbudget from dual
union all
select 'Group1','Name 1','random2',500 from dual
union all
select 'Group1','Name 2','random3', 500 from dual
union all
select 'Group2','Name 3','random4', 500 from dual
union all
select 'Group2','Name 4','random5',500 from dual
union all
select 'Group2','Name 5', 'random6',500 from dual
)
select
maingroup,
subgroup,
random,
(select distinct sum(subgroupbudget) over(partition by maingroup) from t b where a.maingroup=b.maingroup group by maingroup,subgroup,subgroupbudget) groupbudget
from t a
group by maingroup, subgroup ,subgroupbudget, random
order by maingroup, subgroup
As you can see, the with-clause shows a simplified table with data. Now the problem is that the last column is the budget of the subgroup. In the result I need the budget of the maingroup. That means I have to sum all values within the maingroup, but only if the subgroups are different (Here I need some kind of distinct).
Unfortunately a simple
sum(distinct subgroupbudget) over(partition by maingroup)
won't work because the numbers (subgroupbudget) can be the same (like in the example)
Assuming that for a maingroup/subgroup, the subgroupbudget is always the same (or you only take the highest value for the subgroup), this should work:
with t as (select 'Group1' as maingroup,'Name 1' as subgroup, 'random' as random, 500 as subgroupbudget from dual
union all
select 'Group1','Name 1','random2',500 from dual
union all
select 'Group1','Name 2','random3', 500 from dual
union all
select 'Group2','Name 3','random4', 500 from dual
union all
select 'Group2','Name 4','random5',500 from dual
union all
select 'Group2','Name 5', 'random6',500 from dual),
t1 as (select maingroup,
subgroup,
random,
case when row_number() over (partition by maingroup, subgroup order by subgroupbudget desc) = 1 then subgroupbudget
end subgroupbudget
from t)
select maingroup,
subgroup,
random,
sum(subgroupbudget) over (partition by maingroup) groupbudget
from t1;
MAINGROUP SUBGROUP RANDOM GROUPBUDGET
--------- -------- ------- -----------
Group1 Name 1 random 1000
Group1 Name 1 random2 1000
Group1 Name 2 random3 1000
Group2 Name 3 random4 1500
Group2 Name 4 random5 1500
Group2 Name 5 random6 1500
It is effectively saying that for a maingroup/subgroup, you only want to use one of the values (the highest) of the rows in that subgroup in the sum.
Whether it's "better" (i.e. more performant) than your original query is something that you would have to test. Sub-queries are not necessarily a bad thing; they are a tool, and sometimes they're the right tool to use.

Oracle Pivot query gives columns with quotes around the column names. What?

I'm trying to use PIVOT in Oracle and I'm getting a weird result. It's probably just an option I need to set but what I know about Oracle/SQL I could fit into this comment box.
Here's an example of my query:
with testdata as
(
select 'Fred' First_Name, 10 Items from dual
union
select 'John' First_Name, 5 Items from dual
union
select 'Jane' First_Name, 12 Items from dual
union
select 'Fred' First_Name, 15 Items from dual
)
select * from testdata
pivot (
sum(Items)
for First_Name
in ('Fred','John','Jane')
The results come out as I expected except the Column names have single quotes around them (picture from Toad - if I export to Excel the quotes get carried to Excel):
How do I get rid of the single quotes around the column names? I tried taking them out in the "in" clause and I get an error:
in (Fred,John,Jane)
I also tried replacing the single quotes with double quotes and got the same error. I don't know if this is an Oracle option I need to set/unset before running my query or a Toad thing.
you can provide aliases to the new columns in the pivot statement's IN clause.
(NB: This is different from the standard where clause IN() which does not allow aliases.)
with testdata as
(
select 'Fred' First_Name, 10 Items from dual
union
select 'John' First_Name, 5 Items from dual
union
select 'Jane' First_Name, 12 Items from dual
union
select 'Fred' First_Name, 15 Items from dual
)
select * from testdata
pivot (
sum(Items)
for First_Name
in ('Fred' as fred,'John' as john,'Jane' as jane)
)
and also for your aggregate clause which is necessary if you have multiple clauses..
with testdata as
(
select 'Fred' First_Name, 10 Items from dual
union
select 'John' First_Name, 5 Items from dual
union
select 'Jane' First_Name, 12 Items from dual
union
select 'Fred' First_Name, 15 Items from dual
)
select * from testdata
pivot (
sum(Items) itmsum,
count(Items) itmcnt
for First_Name
in ('Fred' as fred,'John' as john,'Jane' as jane)
)
returns
FRED_ITMSUM FRED_ITMCNT JOHN_ITMSUM JOHN_ITMCNT JANE_ITMSUM JANE_ITMCNT
----------- ----------- ----------- ----------- ----------- -----------
25 2 5 1 12 1
Of course you can then go full circle and use standard oracle aliasing and rename them to whatever you like including putting quotes back in again..
with testdata as
(
select 'Fred' First_Name, 10 Items from dual
union
select 'John' First_Name, 5 Items from dual
union
select 'Jane' First_Name, 12 Items from dual
union
select 'Fred' First_Name, 15 Items from dual
)
select FRED_ITMSUM "Fred's Sum", FRED_ITMCNT "Fred's Count"
, JOHN_ITMSUM "John's Sum", JOHN_ITMCNT "John's Count"
, JANE_ITMSUM "Janes's Sum", JANE_ITMCNT "Janes's Count"
from testdata
pivot (
sum(Items) itmsum,
count(Items) itmcnt
for First_Name
in ('Fred' as fred,'John' as john,'Jane' as jane)
)
gives
Fred's Sum Fred's Count John's Sum John's Count Janes's Sum Janes's Count
---------- ------------ ---------- ------------ ----------- -------------
25 2 5 1 12 1