Oracle SUM returns false summary with identicals values returing from an SELECT UNION - sql

I am facing a problem with SUM statement.
This query returns MY_ID = 1 and QTY = 7
select my_id, sum(qty) qty
from
(
select 1 my_id ,2 qty from dual
union
select 1 my_id, 5 qty from dual
)
group by my_id;
But this one returns MY_ID = 1 and QTY = 5 instead of QTY = 10.
select my_id, sum(qty) qty
from
(
select 1 my_id ,5 qty from dual
union
select 1 my_id, 5 qty from dual
)
group by my_id;
How can I summary the two quantity in case of the two values are the same?

Use union all:
select my_id, sum(qty) qty
from
(
select 1 my_id ,5 qty from dual
union all
select 1 my_id, 5 qty from dual
)
group by my_id;

Try using union all:
The below works:
select my_id, sum(qty) qty
from
(
select 1 my_id ,5 qty from dual
union all
select 1 my_id, 5 qty from dual
)
group by my_id;
this is because 5 union 5 is always 5. if you do union all it includes everything irrespective of it being the same!

In the second query, the two rows in the union are identical.
There are two forms of UNION: UNION ALL and UNION DISTINCT. Which one is the default varies, but it looks like you're getting a UNION DISTINCT, which since the two (1, 5) rows are the same is only returning one of them. Change it to:
select my_id, sum(qty) qty
from
(
select 1 my_id ,5 qty from dual
union ALL
select 1 my_id, 5 qty from dual
)
group by my_id;
That should give you what you want: (1, 10).
EDIT: Briefly I had union DISTINCT in the query which was wrong! Now corrected....

Related

Is there a reason I am getting no rows selected for this NOT IN nested query

Query:
SELECT teamid
FROM team
WHERE teamid NOT IN (SELECT team_teamid FROM gymnast);
Tables:
TEAM table
Gymnast table
I have been trying to figure this out for a while, any information would be greatly appreciated. Thank you!
The fiddle for oracle
val NOT IN (some list) means the value does NOT match every entry in the list.
But if one entry in the list is null, value <> null can never be true for that entry, which means the entire NOT IN expression can never be true.
See the following examples:
WITH team (teamid) AS (
SELECT 1 FROM DUAL UNION
SELECT 2 FROM DUAL UNION
SELECT 3 FROM DUAL UNION
SELECT 4 FROM DUAL
)
, gymnast (team_teamid) AS (
SELECT 1 FROM DUAL UNION
SELECT 2 FROM DUAL UNION
SELECT 2 FROM DUAL UNION
SELECT 4 FROM DUAL
)
SELECT teamid FROM team WHERE teamid NOT IN (SELECT team_teamid FROM gymnast)
;
Result:
TEAMID
3
Case 2 with nulls in the list:
WITH team (teamid) AS (
SELECT 1 FROM DUAL UNION
SELECT 2 FROM DUAL UNION
SELECT 3 FROM DUAL UNION
SELECT 4 FROM DUAL
)
, gymnast (team_teamid) AS (
SELECT 1 FROM DUAL UNION
SELECT 2 FROM DUAL UNION
SELECT null FROM DUAL UNION
SELECT 4 FROM DUAL
)
SELECT teamid FROM team WHERE teamid NOT IN (SELECT team_teamid FROM gymnast)
;
Result:
TEAMID
No rows in result, due to the gymnast with a null team_teamid.

How to count distinct flags in the sql

BELOW IS SNIPPET OF MY DATA
Here is the sample creation code of testing.
CREATE TABLE MYGROUP ( Category,PERSON,Flag ) AS
SELECT 'Cat1','A','1' FROM DUAL
UNION ALL SELECT 'Cat1','A','0' FROM DUAL
UNION ALL SELECT 'Cat1','A','1' FROM DUAL
UNION ALL SELECT 'Cat1','B','1' FROM DUAL
UNION ALL SELECT 'Cat1','B','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','B','1' FROM DUAL
UNION ALL SELECT 'Cat2','B','1' FROM DUAL
UNION ALL SELECT 'Cat2','B','0' FROM DUAL
UNION ALL SELECT 'Cat3','X','0' FROM DUAL
UNION ALL SELECT 'Cat3','Y','0' FROM DUAL;
Desired Output:
Category - Count of Distinct Persons with Flag = 1
Cat1 - 2
Cat2 - 1
Cat3 - 0
I need to get my code in Big query to get distinct counts of persons. It shouldnt double count.
You can use conditional aggregation
SELECT
Category,
COUNT(DISTINCT CASE WHEN Flag = 1 THEN PERSON END)
FROM MYGROUP
GROUP BY Category;

select rows between two character values of a column

I have a table which shows as below:
S.No | Action
1 | New
2 | Dependent
3 | Dependent
4 | Dependent
5 | New
6 | Dependent
7 | Dependent
8 | New
9 | Dependent
10 | Dependent
I here want to select the rows between the first two 'New' values in the Action column, including the first row with the 'New' action. Like [New,New)
For example:
In this case, I want to select rows 1,2,3,4.
Please let me know how to do this.
Hmmm. Let's count up the cumulative number of times that New appears as a value and use that:
select t.*
from (select t.*,
sum(case when action = 'New' then 1 else 0 end) over (order by s_no) as cume_new
from t
) t
where cume_new = 1;
you can do some magic with analytic functions
1 select group of NEW actions, to get min and max s_no
2 select lead of 2 rows
3 select get between 2 sno (min and max)
with t as (
select 1 sno, 'New' action from dual union
select 2,'Dependent' from dual union
select 3,'Dependent' from dual union
select 4,'Dependent' from dual union
select 5,'New' from dual union
select 6,'Dependent' from dual union
select 7,'Dependent' from dual union
select 8,'New' from dual union
select 9,'Dependent' from dual union
select 10,'Dependent' from dual
)
select *
from (select *
from (select sno, lead(sno) over (order by sno) a
from ( select row_number() over (partition by action order by Sno) t,
t.sno
from t
where t.action = 'New'
) a
where t <=2 )
where a is not null) a, t
where t.sno >= a.sno and t.sno < a.a

Maintaining the order by in union of two ordered by queries

I am trying to run the below query but looks like I am doing something wrong.
(Just modified the sample query for clear understanding)
SELECT name,total,rate
FROM business b
WHERE rate > 100
ORDER BY total DESC
UNION
SELECT name,total,rate
FROM business b
WHERE rate <= 100
ORDER BY rate ASC
Now I want to union of these two queries and in the resultant output in first row the output should come from first query and then output from second query in the same sorted order however the single actual query was giving.
Let me know if it is still unclear. I will try to explain in some more deep level.
It's really simple: Use UNION ALL instead of UNION.
SELECT * FROM (
SELECT name,total,rate
FROM business b
WHERE rate > 100
ORDER BY total DESC) x
UNION ALL
SELECT * FROM (
SELECT name,total,rate
FROM business b
WHERE rate <= 100
ORDER BY rate ASC) y
UNION ALL preserves order as coded.
UNION removes duplicates and does not guarantee order. Most databases actually sort the output (to make duplicate detection easier).
Try this:
select name,total,rate
from (
SELECT name,total,rate, row_number() over(order by total desc) rn, 1 ord
FROM business b
WHERE rate > 100
UNION
SELECT name,total,rate, row_number() over(ORDER BY rate ASC) rn, 2 ord
FROM business b
WHERE rate <= 100
)
order by ord, rn
Add a branch flag in each of the inner branches, and sort by that first:
SELECT * FROM
(
SELECT * FROM
(
SELECT 'd' AS char1 , 2 AS sortcol, 1 AS branch FROM dual
UNION
SELECT 'c' AS char1 , 1 AS sortcol, 1 AS branch FROM dual
ORDER BY sortcol
) Inner1
UNION
SELECT * FROM
(
SELECT 'a' AS char1 , 1 AS sortcol, 2 as branch FROM dual
UNION
SELECT 'b' AS char1 , 2 AS sortcol, 2 as branch FROM dual
ORDER BY sortcol
) Inner2
) Outer
ORDER BY Outer.branch, Outer.SORTCOL;
CHAR1 SORTCOL BRANCH
----- ---------- ----------
c 1 1
d 2 1
a 1 2
b 2 2
Obviously you can replace * in the outer query to only get the columns you want in the result set, excluding both branch and sortcol.
SQL Fiddle with both queries for comparison.
EDIT: Try conditional OR clause as here. This will also avoid unions.
SELECT NAME, TOTAL, RATE FROM BUSINESS B
ORDER BY
CASE
WHEN rate > 100 THEN total
WHEN rate <= 100 THEN rate
END
Instead of using multiple layers of select query, Just include an additional field called TAB_ID in your select query and sort using that field as below:
SELECT CHAR1 FROM
(
SELECT 'd' AS char1 , 2 AS sortcol, '1' AS TAB_ID FROM dual
UNION
SELECT 'c' AS char1 , 1 AS sortcol, '1' AS TAB_ID FROM dual
UNION
SELECT 'a' AS char1 , 1 AS sortcol, '2' AS TAB_ID FROM dual
UNION
SELECT 'b' AS char1 , 2 AS sortcol, '2' AS TAB_ID FROM dual
) ORDER BY TAB_ID, SORTCOL;
Check the result out from Working SQL Fiddle Here
To correct your original query and avoid the missing paranthesis error, use below query, but it does not seem to provide the right output:
select * from
(
SELECT * FROM
(
SELECT 'd' AS char1 , 2 AS sortcol FROM dual
UNION
SELECT 'c' AS char1 , 1 AS sortcol FROM dual
) Inner1
ORDER BY Inner1.sortcol
)
UNION
select * from
(
SELECT * FROM
(
SELECT 'a' AS char1 , 1 AS sortcol FROM dual
UNION
SELECT 'b' AS char1 , 2 AS sortcol FROM dual
) Inner2
ORDER BY Inner2.sortcol
) ;

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.