LIMIT by GROUP in SQl (postgresql) - sql

Below is my table students (having 100K+ rows in orignal, showing just a set):
id
name
class
marks
1
Ryan
5
8
2
Max
5
7
3
Max1
5
10
4
Ryan1
6
8
5
Max2
6
10
6
Ryan2
6
7
7
Ryan3
7
8
8
Max3
7
10
9
Ryan4
7
7
I want to fetch two rows per class ( 5 & 7) having marks <= 10 , also sorted by class, marks ASC
So, the expected result will be:-
id
name
class
marks
1
Ryan
5
8
3
Max1
5
10
7
Ryan3
7
8
8
Max3
7
10
To execute below I tried:-
SELECT DISTINCT t_out.class, t_top.marks, t_top.name
FROM students t_out
JOIN LATERAL (
select *
from students t_in
where t_in.class = t_out.class
ORDER BY t_in.id ASC
) t_top ON TRUE
WHERE t_top.marks <= 10
AND (t_out.class = 5 OR t_out.class = 7)
ORDER BY t_top.marks DESC LIMIT 2;
Result on original database:- it's loading since long time
Result on sample :- Error: near line 20: near "(": syntax error
Is 10 the highest marks?

You would use row_number():
select s.*
from (select s.*,
row_number() over (partition by class order by marks desc) as seqnum
from students s
where marks < 10 and class in (5, 7)
) s
where seqnum <= 2
order by class, marks;
Note: Your question is a little confusing. You seem to want two rows with the highest marks per class ordered in descending order by marks.
EDIT:
Based on your comment:
select s.*
from (select s.*,
row_number() over (partition by class order by marks desc) as seqnum,
count(*) over (partition by class) as cnt
from students s
where marks < 10
) s
where seqnum <= 2 and cnt >= 2
order by class, marks;

Related

Select commonly chosen desires collage by students after first 5 rows each group

With subquery I need to select after first five rows for each group of id_student and must common values of id_desireCollage between id_student.
More explain : select common collages for each student desires after his five chosen desires
ID
id_desireCollage
id_student
1
1
1
2
2
1
3
3
1
4
4
1
5
5
1
6
8
1
7
9
1
8
7
1
9
2
2
10
12
2
11
1
2
12
3
2
13
6
2
14
5
2
15
8
2
16
9
2
17
7
2
18
4
3
19
3
3
20
2
3
21
1
3
22
8
3
23
9
3
24
7
3
25
5
3
Something like
select id_desireCollage
from
(select *
from desires ds
where ds.id_desireCollage = desires.id_desireCollage)
group by (id_student)
having count(*) > 5
Expected result is:
id_desireCollage
7
9
Try the following:
select id_desireCollage
from
(
select d.*,
row_number() over (partition by id_student order by ID) as rn
from desires d
) T
where rn > 5
group by id_desireCollage
order by count(*) desc
fetch first 1 row with ties
If you don't want to use the row number function (as you commented), you may try the following - supposing there are no gaps in the ID column:
select id_desireCollage
from desires d
where id >=
(
select min(id)+5
from desires t
where t.id_student = d.id_student
)
group by id_desireCollage
order by count(*) desc
fetch first 1 row with ties
See demo
As suggested by #MatBailie, if you meant by common, that all students have selected the id_desireCollage value then you could use the following:
select id_desireCollage
from desires d
where id >=
(
select min(id)+5
from desires t
where t.id_student = d.id_student
)
group by id_desireCollage
having count(*)=
(
select count(distinct id_student)
from desires
)

pick all positive least numbers from data set [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Oracle group using min date
(3 answers)
GROUP BY with MAX(DATE) [duplicate]
(6 answers)
Closed 3 years ago.
I have below data in a table
ID AMOUNT DAYS
1 10 1
1 20 2
1 30 3
1 1 4
2 34 1
2 234 2
2 234 3
2 34 4
3 3 1
3 3 2
3 23 3
3 20 4
I want below results as all amounts which have least days of a ID
ID AMOUNT DAYS
1 10 1
2 34 1
3 3 1
Please suggest a sql query to pick this desired output
For your example, you can simply do:
select t.*
from t
where t.days = 1;
If 1 is not fixed, then a correlated subquery is one method:
select t.*
from t
where t.days = (select min(t2.days) from t t2 where t2.id = t.id);
Another method is aggregation:
select t.id, min(t.days) as min_days,
min(t.amount) keep (dense_rank first order by t.days asc) as min_amount
from t
group by t.id;
Of course row_number()/rank() is another alternative.
With an index on (id, days) and a large table, one of the above methods may be faster in practice.
You can use rank() function
select ID, Amount, Days from
(
select rank() over (partition by ID order by days) as rn,
t.*
from tab t
)
where rn = 1;
Demo
First group by id to find the min days for each id and then join to the table
select t.*
from tablename t inner join (
select id, min(days) days
from tablename
group by id
) g on g.id = t.id and g.days = t.days

Oracle query to find the top 2 item per group [duplicate]

This question already has answers here:
Get top results for each group (in Oracle)
(5 answers)
ORACLE - find a concrete result inside a LOOP (OR SIMILAR) [duplicate]
(1 answer)
Closed 2 years ago.
I want to right a sql query to find the top 2 item per group in oracle.
The table contains:
P_id Price GroupX
1 10 a
2 5 a
3 5 a
4 4 b
5 8 b
6 6 b
Output should be:
P_id Price GroupX
1 10 a
2 5 a
5 8 b
6 6 b
What I have tried is:
Select * from table
group by GroupX
order by price desc
limit 2
How about
Select rnk,P_id, Price, GroupX
from (
select P_id, Price, GroupX,
RANK () OVER (PARTITION BY GroupX
ORDER BY Price DESC
) AS rnk
from table
)
where rnk <= 2;
You can use the ROW_NUMBER() analytic function:
SELECT p_id,
price,
GroupX
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY groupX
ORDER BY Price DESC ) AS rn
FROM table_name t
)
WHERE rn <= 2;

Fetch a fixed number of rows in SQL query in Oracle [duplicate]

This question already has answers here:
Paging with Oracle
(7 answers)
Closed 8 years ago.
Please help me to write an SQL query in the Oracle database. There is table called tbl and it has 12 rows. I want to select first 4 rows first then next 4 and the last 4 rows.
Can any anyone tell me how can I do this in Informix.
EDIT: now should be fixed with 3-level select:
select * from (
select q1.*, rownum as rn from ( --get correct rownum
select * from tbl order by column --get correct order
) q1
) q2
where q2.rn between 1 and 4; -- filter
for first part.
For second and third part:
where q2.rn between 5 and 8
where q2.rn between 9 and 12
There is nothing called as first rows, last rows, "n" rows unless you explicitly specify an ORDER BY and then select the required rows.
Top-n Row Limiting feature in Oracle 12c on ward:
SQL> select * from order_test order by val;
VAL
----------
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
20 rows selected.
First 4 rows :
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 FETCH FIRST 4 ROWS ONLY;
VAL
----------
1
1
2
2
Next 4 rows(look at OFFSET) :
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
3
3
4
4
Finally, next 4 rows with OFFSET 8 rows :
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 8 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
5
5
6
6
You can use rownum:
select * from (select t.*, rownum rn from tbl t) where rn between 1 and 4;
/
select * from (select t.*, rownum rn from tbl t) where rn between 5 and 8;
/
select * from (select t.*, rownum rn from tbl t) where rn between 9 and 12;
/
If you're using order by clause then use row_number() (documentation)
select * from (select t.*, row_number() over (order by column_name) rn from tbl t) where rn between 1 and 4;
/
select * from (select t.*, row_number() over (order by column_name) rn from tbl t) where rn between 5 and 8;
/
select * from (select t.*, row_number() over (order by column_name) rn from tbl t) where rn between 9 and 12;
/

Select count() max() Date

I have a table with shifts history along with emp ids.
I'm using this query to retrieve a list of employees and their total shifts by specifying the range to count from:
SELECT ope_id, count(ope_id)
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy') and ope_shift_date
<= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
which gives
OPE_ID COUNT(OPE_ID)
1 14
2 7
3 6
4 6
5 2
6 5
7 2
8 1
9 2
10 4
10 rows selected.
How do I choose the employee with the highest number of shifts under the specified range date?
Assuming your version of Oracle is new enough to support common table expressions:
With ShiftCounts As
(
SELECT ope_id, count(ope_id) ShiftCount
, ROW_NUMBER() OVER( ORDER BY Count(ope_id) Desc ) ShiftRank
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy')
and ope_shift_date <= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
)
Select ope_id, ShiftCount
From ShiftCounts
Where ShiftRank = 1
something like this maybe:
SELECT TOP 1 ope_id, Count(ope_id)
FROM operator_shift
WHERE ope_shift_date >=to_date( '01-MAR-10','dd-mon-yy') and ope_shift_date
<= to_date('31-MAR-10','dd-mon-yy')
GROUP BY OPE_ID
ORDER BY Count(ope_id) DESC
Use:
SELECT t.ope_id,
t.num
FROM (SELECT os.ope_id,
COUNT(os.ope_id) AS num
FROM OPERATOR_SHIFT os
WHERE os.ope_shift_date BETWEEN TO_DATE('01-MAR-10','dd-mon-yy')
AND TO_DATE('31-MAR-10','dd-mon-yy')
GROUP BY os.ope_id
ORDER BY num DESC) t
WHERE ROWNUM = 1
Reference:
Ask Tom: On ROWNUM and Limiting Results