Skip to Main Content
  • Questions
  • Selecting top 2 distinct records in a descending ordered query

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Sinan.

Asked: February 01, 2013 - 10:08 am UTC

Last updated: February 11, 2013 - 10:08 am UTC

Version: 11.2.0.2

Viewed 1000+ times

You Asked

Hello Tom,

I hope all is well.

The database is 11.2.0.2 Enterprise Edition 64Bit.

I know the subject text is not entirely about what I am trying to achive here, but I did not know how to word it without actually asking the question.

I have a table:

create table tbl_history (object_id int, seq int, emp_id int );

with this data in it:

insert into tbl_history values (100, 9, 625);
insert into tbl_history values (100, 7, 625);
insert into tbl_history values (100, 6, 33);
insert into tbl_history values (100, 5, 33);
insert into tbl_history values (100, 4, 241);
insert into tbl_history values (100, 3, 125);

insert into tbl_history values (200, 5, 625);
insert into tbl_history values (200, 4, 33);
insert into tbl_history values (200, 3, 33);
insert into tbl_history values (200, 2, 241);
insert into tbl_history values (200, 1, 125);

The query needs to return only "2 records per object_id" and those two records should be sorted by "seq" column in Descending order and the top 2 DISTINCT "emp_id"s will be listed. Based on the sample data above, it should look like:

object_id seq emp_id
--------- --- ------
100 9 625
100 6 33
200 5 625
200 4 33

Please let me know if the question makes sense and wether this could be achieved in a single SQL statement without writing a PL/SQL function.

Thank you,
Sinan

and Tom said...

ops$tkyte%ORA11GR2> select *
  2    from (
  3  select object_id, max(seq) mseq, emp_id, dense_rank() over (partition by object_id order by max(seq) DESC ) dr
  4    from tbl_history
  5   group by object_id, emp_id
  6         )
  7   where dr <= 2
  8   order by object_id, mseq desc
  9  /

 OBJECT_ID       MSEQ     EMP_ID         DR
---------- ---------- ---------- ----------
       100          9        625          1
       100          6         33          2
       200          5        625          1
       200          4         33          2


Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Mapping your brain and analytics

Reader, February 11, 2013 - 5:03 am UTC

That's wonderful, Tom. I really want to do your brain mapping and see how it works when you have to write a query. Would your brain just start commanding the SQL statements which you type in or you would analyze the query completely and then start writing the query? I wish you write a book exclusively on "How to write a SQL". By the way, I am a proud owner of your Expert Oracle book.

Regards
Tom Kyte
February 11, 2013 - 10:08 am UTC

I think it is just experience and recognizing common patterns that happen all of the time.

I can sort of see the answer - because I've done this query many times before (or something so similar)

The query needs to return only "2 records per object_id" and those two records should be sorted by "seq" column in Descending order and the top 2 DISTINCT "emp_id"s will be listed.

lots of triggers in there - "2 records per object_id" - I know I'm probably using row_number, rank or dense_rank (probably dense_rank, it applies most often) partitioned by object_id. I could have actually used any of the three in this case if I assume that SEQ is unique in object_id...

"sort by seq", another trigger... I'm going to sort by seq desc.

"top 2 distinct emp_id's" - I'm going to distinct or aggregate here.


so, knowing that analytics are done after aggregation, I needed to generate the set of distinct emp_ids with seq's by object_id - that was

select object_id, emp_id, max(seq) from tbl_history group by object_id, emp_id

I could have put that into an inline view and then used dense_rank() - but didn't need to. I just ranked the SEQ's (the max seq's - one seq per emp_id) and kep the first two for each object_id.

A typical "top-n by some dimension" query.

Good learning path

A reader, February 11, 2013 - 1:17 pm UTC

Thanks Tom. I see where you are coming from---but that's what many DBA's like me lack. I am sure most of the DBA's decide on "hat" and then make the "head" suit the hat when it comes to SQL writing/tuning, rather than design a hat for head size. To be honest, I have learned many many things with efficient SQL writing from your site and your book and I have received many accolades at work by applying your SQL techniques to lot of challenging situations with production application. Even today, when it comes to efficient SQL writing or understanding the internals of Oracle, I immediately pick up your book for reference. Please consider writing a book exclusively on "How to write a SQL".

By the way, thanks once again for your service to Oracle peers.

Best Regards

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.