I have a sample table with similar structure & data as shown below:
+------+---------+-------------+------------+
| S_ID | S_NAME | SUBJECT | MARK_V
Analytic function ROW_NUMBER
can be used to group rows by S_NAME
(as you want to get maximum mark per student), and sort marks in descending order so that the max value raises to the top (i.e. gets row number = 1).
Then select rows with that row number value.
SQL> with test (s_id, s_name, subject, mark_value) as
2 (select 1, 'stud', 'sub_1' , 50 from dual union all
3 select 2, 'stud', 'sub_2' , 60 from dual union all
4 select 3, 'stud', 'sub_3' , 70 from dual union all
5 select 4, 'stud_1', 'sub_1', 40 from dual union all
6 select 5, 'stud_1', 'sub_2', 50 from dual union all
7 select 6, 'stud_2', 'sub_2', 40 from dual
8 )
9 select s_id, s_name, subject, mark_value
10 from (select s_id, s_name, subject, mark_value,
11 row_Number() over (partition by s_name order by mark_value desc) rn
12 from test
13 )
14 where rn = 1;
S_ID S_NAME SUBJE MARK_VALUE
---------- ------ ----- ----------
3 stud sub_3 70
5 stud_1 sub_2 50
6 stud_2 sub_2 40
SQL>
if your database version doesn't support analytic functions, there's another option which isn't that good as it selects from the same table twice. You won't notice the difference if there aren't that many rows in a table, but performance will suffer on large data sets.
<snip>
9 select s_id, s_name, subject, mark_value
10 from test
11 where (s_name, mark_value) in (select s_name, max(mark_value) max_mark
12 from test
13 group by s_name);
S_ID S_NAME SUBJE MARK_VALUE
---------- ------ ----- ----------
3 stud sub_3 70
5 stud_1 sub_2 50
6 stud_2 sub_2 40
SQL>
use row_number()
window function
select * from
( select *,
row_number()over(partition by s_name order by MARK_VALUE desc) rn
from table_name
) t where t.rn=1
or you can use corelated subquery
select t1.* from table_name t1
where t.MARK_VALUE=(select max(MARK_VALUE) from table_name t2 where t2.S_NAME=t1.S_NAME)
You can use group by
and keep
:
select max(s_id) keep (dense_rank first order by mark desc) as s_id,
s_name,
max(subject) keep (dense_rank first order by mark desc) as subject,
max(max_mark)
from t
group by s_name;
keep
is an Oracle extension that allows functionality like first_value()
and last_value()
for aggregation functions. In my experience, it is quite fast.
Use row_number()
select * from
(
select *,row_number() over(partition by s_name order by MARK_VALUE desc) as rn
from tablename
)A where rn=1
Please try this.
Select B.* from @tbl AS B
INNER JOIN(
Select S_Name,MAX(MARK_VALUE) AS MARK_VALUE from @tbl Group by S_Name) AS A
ON A.S_name=B.S_Name
AND A.MARK_VALUE = B.MARK_VALUE