0

I have four tables :

  1. Batch(batch_id, batch_start_date, batch_strength, course_id)
  2. Course(course_id, course_name, course_category, course_fees, course_duration)
  3. Enrollment(batch_id, student_id, enrollment_date)
  4. Student(student_id, student_name, student_mail, student_date_of_birth, student_city, student_phone, student_qualification)

Now according to the question I have to display the coursename which has 2nd highest revenue collection

According to the above question here is my solution

select c.course_name, c.course_fees*count(c.course_name) Total_Revenue
from course c join(batch b join enrollment e 
on b.batch_id=e.batch_id) 
on c.course_id=b.course_id
group by c.course_name, c.course_fees
order by Total_Revenue desc ;

Now the problem is I am not able to extract the second row from the resulting table from the above code. How can I extract second row from the above resulting table?(Answer specific to Oracle 11g is appreciated)

MT0
  • 143,790
  • 11
  • 59
  • 117

3 Answers3

5

Use row_number():

select *
from (select c.course_name, c.course_fees*count(c.course_name) as Total_Revenue,
             row_number() over (order by c.course_fees*count(c.course_name)) as seqnum
      from batch b join
           enrollment e 
           on b.batch_id = e.batch_id join
           course c
           on c.course_id=b.course_id
      group by c.course_name, c.course_fees
     ) bec
where seqnum = 2;

If you could have ties for first and still want the second place, then use dense_rank() instead of row_number().

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • 1
    @sagi But Gordon points out that it can also be easily changed to `dense_rank()` for the case of ties not to mention that this is a portable approach. – shawnt00 Jan 27 '16 at 15:31
  • @sagi you can't use rownum unless you return every row between 1 and the deisred row number. Use either the `row_number` or `dense_rank` analytic functions instead – Sentinel Jan 27 '16 at 16:02
3

Non-analytic solution just for the fun of it:

with r as (
    select min(c.course_name) as course_name, min(c.course_fees) * count(*) as revenue
    from
        course c
        inner join batch b on b.course_id = c.course_id
        inner join enrollment e on e.batch_id = b.batch_id
    group by c.course_id
)
select course_name, revenue
from r
where revenue = (select max(revenue) from r where revenue < (select max(revenue) from r))

This handles ties (in both first and second place.) I'm also going out on a limb and assuming that you really intended to group by course_id. This looks more like a class exercise so I don't expect there's any complication like historical fee information or anything like that going on.

EDIT Based on your comment below it sounds like you may have multiple courses with the same name and different fees. I suspect your original query will not work correctly since you are also grouping on course_fees.

Notice the change below uses a grouping on course_name and a summation on course_fees:

with r as (
    select course_name, sum(c.course_fees) as revenue
    from
        course c
        inner join batch b on b.course_id = c.course_id
        inner join enrollment e on e.batch_id = b.batch_id
    group by c.course_name
)
select course_name, revenue
from r
where revenue = (select max(revenue) from r where revenue < (select max(revenue) from r))
shawnt00
  • 16,443
  • 3
  • 17
  • 22
  • I intended to group by course_name here as course_id is insignificant here and doesn't yield proper results – user5451270 Jan 27 '16 at 16:13
  • @user5451270 `course_id` appears to be your primary key so. Generally you wouldn't want to group by a name value. Although I imagine that all the course names are unique here I would argue it's a bad habit (unless you really need to) and outside of the classroom group on the primary key is probably going to be more efficient for the database engine. I'm curious how it doesn't give the proper result if you don't mind sharing. – shawnt00 Jan 27 '16 at 16:17
  • When i group by ``course_id`` it doesn't calculate the total revenue as i expect it to be. e.g Like under ``course_name`` 2 ``sql server`` are shown with separate revenues ``9000`` and ``7000`` whereas i want only 1 ``sql server`` to show with revenue as ``16000`` and no the ``course_name`` column is not unique – user5451270 Jan 27 '16 at 16:32
  • @user5451270 I hope you'll take a look at my edit. But if you do indeed have multiple course rows with the same name and different fees then the grouping in your original query is incorrect. – shawnt00 Jan 27 '16 at 17:20
  • Ya sure I'll look into your edit and try to find out if i have any mistakes from my end – user5451270 Jan 27 '16 at 18:15
0

You can also create you own aggregate function. The Oracle docs mention function SecondMax as an example. But if there is a proper index, then row_numer function might give better execution plan.

ibre5041
  • 4,903
  • 1
  • 20
  • 35