GROUP BY ID and select MAX - sql

Good Evening,
I am working on a table like this in Oracle:
ID
BALANCE
SEQ
1
102
13
1
119
15
2
50
4
3
20
11
3
15
10
3
45
9
4
90
5
5
67
20
5
12
19
6
20
1
I want to select, for each ID, the BALANCE having MAX(SEQ).
So final result would be:
ID
BALANCE
SEQ
1
119
15
2
50
4
3
20
11
4
90
5
5
67
20
6
20
1
How can I do that?
I've tried several Group by queries but with no success.
Thanks for any help

One method is aggregation using keep:
select id,
max(balance) keep (dense_rank first order by seq desc) as balance,
max(seq)
from t
group by id;

You may use normal rank()
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from t
) WHERE ranks = 1
sample demo
SELECT ID, BALANCE, SEQ FROM (
SELECT ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
FROM (
SELECT 1 ID, 102 BALANCE, 13 SEQ FROM dual UNION all
SELECT 1, 119, 15 FROM dual UNION all
SELECT 2, 50, 4 FROM dual UNION all
SELECT 3, 20, 11 FROM dual UNION all
SELECT 3, 15, 10 FROM dual UNION all
SELECT 3, 45, 9 FROM dual UNION all
SELECT 4, 90, 5 FROM dual UNION all
SELECT 5, 67, 20 FROM dual UNION all
SELECT 5, 12, 19 FROM dual UNION all
SELECT 6, 20, 1 FROM dual
)
) WHERE ranks = 1
you can add it in your Big query as below
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from (**YOUR BIG QUERY HERE**)
) WHERE ranks = 1

Related

Difference of SUM while doing multiple group by

I have table like below
Pid
qid
count
sum
11
13
5
800
12
13
7
300
11
14
3
100
12
14
5
200
I need to get the difference of sum while doing group by using pid and qid
Like
SELECT
/* ??? */
FROM
tbl
GROUP BY
pid,
qid
Id1
id2
qid
difference
11
12
13
500
Use conditional aggregation:
SELECT 11 AS id1,
12 AS id2,
qid,
ABS(SUM(CASE pid WHEN 11 THEN value WHEN 12 THEN -value END)) AS diff
FROM tbl
GROUP BY qid;
Which, for the sample data:
CREATE TABLE tbl (Pid, qid, value) AS
SELECT 11, 13, 160 FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 12, 13, 43 FROM DUAL CONNECT BY LEVEL <= 6 UNION ALL
SELECT 12, 13, 42 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 11, 14, 30 FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 11, 14, 40 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 12, 14, 40 FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID1
ID2
QID
DIFF
11
12
13
500
11
12
14
100
fiddle
Looks like you want a difference of sum between all pairs of different pid per each qid. The easiest and most reliable solution in this case would be to use a simple self join, like this:
with agg as (
select pid, qid
, count(*) as cnt
, sum(?) as sum1
from your_table
group by pid, qid
)
select
t1.pid as id1
,t2.pid as id2
,abs(t2.sum1-t1.sum1) as diff
from agg t1
join agg t2
on t1.qid=t2.qid
and t1.pid<t2.pid
Obviously, we could optimize it if we were sure that you have just 2 different PIDs per each qid, but you haven't mention it

Oracle query to display count and start, end of sequence

I know basics of Oracle and I am a java developer, I can do the following operation/task in java by fetching the data and iterate over it. But I would like to know that is there any way to show start and end of the sequence and the difference in between start and end using SQL(Oracle) query.
Let's say I have a table TB1 with a column seq which contains some sequential numbers
SEQ
------
1
2
3
7
8
9
14
19
20
Is there any way to display the sequence start, end and the count as follows.
Start | end | count
---------------------
1 3 3
7 9 3
14 14 1
19 20 2
Please give me some pointer if it is achievable or not.
Thanks in advance.
Yes. You could do it easily using TABIBITOSAN method.
SELECT MIN(seq)
,MAX(seq)
,count(*)
FROM (
SELECT seq
,seq - row_number() OVER (
ORDER BY seq
) grp
FROM t
)
GROUP BY grp
ORDER BY 1;
Demo
You should write a procedure, and loop in through cursor. Keep three temp variable, wherever there is break in seq(make sure you do Order by on Seq column), and insert the start,end and count into other table.
See similar example that may be helpful to you.
SQL> WITH cte_table (seq) AS (
2 SELECT 1 FROM dual UNION ALL
3 SELECT 2 FROM dual UNION ALL
4 SELECT 3 FROM dual UNION ALL
5 SELECT 7 FROM dual UNION ALL
6 SELECT 8 FROM dual UNION ALL
7 SELECT 9 FROM dual UNION ALL
8 SELECT 14 FROM dual UNION ALL
9 SELECT 19 FROM dual UNION ALL
10 SELECT 20 FROM dual),
11 table_ AS (
12 SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM cte_table)
13 SELECT MIN(seq) "START",
14 MAX(seq) "END",
15 COUNT(*) "COUNT"
16 FROM table_
17 GROUP BY grp
18 ORDER BY 1;
Output:
START END COUNT
---------- ---------- ----------
1 3 3
7 9 3
14 14 1
19 20 2
Using your table, the query will be
WITH table_ AS (
SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM tb1)
SELECT MIN(seq) "START",
MAX(seq) "END",
COUNT(*) "COUNT"
FROM table_
GROUP BY grp
ORDER BY 1;

Oracle - How to use avg(count(*)) on having clause or where clause?

I have data like this:
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
1 2 2 1,5
3 5 2 2
3 6 2 2
5 11 4 2
5 12 1 2
5 13 1 2
Thats from this query:
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id;
How to make a limitation on that query. What i want to make is
If the attendance is below average attendance show result
I already tried avg(count(*)) over (partition by concert_id) to having clause but gave me an error group function is too deep
It's easy to get the desired results by applying just one nesting :
select * from
(
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id
)
where attendance < avg_attendance_each_concert
D e M o
Include an "intermediate" table, a query which returned the correct result in your last question. Then select values - which satisfy a new condition - from it.
SQL> with booking (concert_id, event_id, customer_id) as
2 (select 1, 1, 10 from dual union
3 select 1, 2, 10 from dual union
4 select 1, 2, 20 from dual union
5 --
6 select 3, 5, 10 from dual union
7 select 3, 5, 20 from dual union
8 select 3, 6, 30 from dual union
9 select 3, 6, 40 from dual union
10 --
11 select 5, 11, 10 from dual union
12 select 5, 11, 20 from dual union
13 select 5, 11, 30 from dual union
14 select 5, 11, 40 from dual union
15 select 5, 12, 50 from dual union
16 select 5, 13, 60 from dual
17 ),
18 inter as
19 (select concert_id, event_id, count(customer_id) attendance,
20 avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
21 from booking
22 group by concert_id, event_id
23 )
24 select concert_id, event_id, attendance, avg_attendance_each_concert
25 from inter
26 where attendance < avg_attendance_Each_concert
27 order by event_id;
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
5 12 1 2
5 13 1 2
SQL>

Contiguous Group By

I have the following table:
SELECT *
FROM mytable
ORDER BY id;
id name code time
1 A 111 1
2 A 111 2
3 A 888 3
4 A 888 4
5 A 888 5
6 A 888 6
7 A 888 7
8 A 111 8
9 A 111 9
10 A 111 10
I need to get a result like this:
name code times_between
A 111 1,2
A 888 3,7
A 111 8,10
Is it possible to group by "chunks"?
I need to make a distinction based on time, so I can't just group by name,code and get the first and last element only.
One way is this:
with the_table(id, name , code , time) as(
select 1, 'A',111 , 1 union all
select 2, 'A',111 , 2 union all
select 3, 'A',888 , 3 union all
select 4, 'A',888 , 4 union all
select 5, 'A',888 , 5 union all
select 6, 'A',888 , 6 union all
select 7, 'A',888 , 7 union all
select 8, 'A',111 , 8 union all
select 9, 'A',111 , 9 union all
select 10, 'A',111 , 10
)
select name, code, min(time) ||','|| max(time) from (
select name, code, time, id,
row_number() over(order by id) -
row_number() over(partition by name , code order by id) as grp
from the_table
) t
group by name, code, grp
order by min(id)
(I forgot and just can't find/remember the name of technique, which creates groups grp)

How to get Nth big item [duplicate]

This question already has answers here:
Finding Nth Minimum of a Varchar value in Oracle
(2 answers)
Closed 9 years ago.
I am getting 11th older person from users table with the following SQL statement
select MAX(age)
from ( select *
from (select *
from users
order by age asc)
where rownum <12)
is there a simplified and efficient query to get 11th older person with full information?
USING
Oracle 11G
WITH AgeOrderedPersons AS (
SELECT usr.*
,ROW_NUMBER() OVER (ORDER BY Age) AS Number
FROM Users usr
)
SELECT *
FROM AgeOrderedPersons
WHERE Number = 11
If you want all users with same age use DENSE_RANK() instead of ROW_NUMBER()
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE users ( age, x ) AS
SELECT 20, 1 FROM DUAL
UNION ALL SELECT 80, 2 FROM DUAL
UNION ALL SELECT 47, 3 FROM DUAL
UNION ALL SELECT 33, 4 FROM DUAL
UNION ALL SELECT 24, 5 FROM DUAL
UNION ALL SELECT 7, 6 FROM DUAL
UNION ALL SELECT 102, 7 FROM DUAL
UNION ALL SELECT 99, 8 FROM DUAL
UNION ALL SELECT 90, 9 FROM DUAL
UNION ALL SELECT 28, 10 FROM DUAL
UNION ALL SELECT 46, 11 FROM DUAL
UNION ALL SELECT 54, 12 FROM DUAL
UNION ALL SELECT 67, 13 FROM DUAL
UNION ALL SELECT 17, 14 FROM DUAL
UNION ALL SELECT 34, 15 FROM DUAL
UNION ALL SELECT 32, 16 FROM DUAL
UNION ALL SELECT 39, 17 FROM DUAL
UNION ALL SELECT 26, 18 FROM DUAL
UNION ALL SELECT 15, 19 FROM DUAL
UNION ALL SELECT 12, 20 FROM DUAL;
Query 1:
SELECT DISTINCT
NTH_VALUE( age, 11 ) IGNORE NULLS OVER ( ORDER BY age ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS age,
NTH_VALUE( x, 11 ) IGNORE NULLS OVER ( ORDER BY age ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS x
FROM users
Results:
| AGE | X |
|-----|----|
| 34 | 15 |
Query 2:
Include an ordering clause in the statement with ROW_NUMBER():
WITH ranked AS (
SELECT u.*,
ROW_NUMBER() OVER ( ORDER BY age ) AS rn
FROM users u
ORDER BY age
)
SELECT age, x
FROM ranked
WHERE rn = 11
Results:
| AGE | X |
|-----|----|
| 34 | 15 |
Query 3:
WITH ordered AS (
SELECT *
FROM users
ORDER BY age
),
ranked AS (
SELECT o.*,
ROWNUM AS rn
FROM ordered o
WHERE ROWNUM <= 11
)
SELECT age, x
FROM ranked
WHERE rn = 11
Results:
| AGE | X |
|-----|----|
| 34 | 15 |