Oracle SQL select distinct query to return specific number of rows

joaerl picture joaerl · Aug 10, 2011 · Viewed 10.2k times · Source

I have a table in an Oracle database with a timestamp column nextTime and a string column destName. There are more columns as well, but only these two are relevant in this case. I'm trying to design a query that will return distinct destName having nextTime within a specific interval and the returned number of rows should be maximum one thousand. When there are more than one thousand distinct destName within the interval I want the query to return one thousand rows, netiher more nor less.

I actually have a query that is working, but it is way too slow:

select destName 
from (select /*+ index(tblDestNames tbldestnames_destname)*/ distinct destName
from (select /*+ index(tblDestNames tbldestnames_nextTime)*/ destName
from tblDestNames 
where nextTime < :1 and nextTime >= :2 and destName is not null)) 
where rownum <= 1000; 

Any ideas on how to design a smarter query or how to optimize this exisiting one are very much appreciated.

Answer

Uphill_ What &#39;1 picture Uphill_ What '1 · Aug 10, 2011

I am not sure that there is a reason not to simplify the query to this:

select destName 
from (
    select distinct destName
    from tblDestNames 
    where nextTime < :1 and nextTime >= :2 and destName is not null
    )
where rownum <= 1000; 

However, this will not solve your performance issue. The problem is this:

where rownum <= 1000

By replacing rownum with 'rank' and 'over' you would get something like:

select distinct destName
from (
    select
       destName
    from
       (select destName, rank()
        over (order by destName desc ) rnk
        from tblDestNames
        where nextTime < :1 and nextTime >= :2 and destName is not null) 
    where rnk <= 1000;
    )

The bonus is that with 'over' you choose the order of the results that will show and the ones that will not show.

EDIT: Actually it can be further simplified into:

select
   distinct destName
from
   (select destName, rank()
    over (order by destName desc ) rnk
    from tblDestNames
    where nextTime < :1 and nextTime >= :2 and destName is not null) 
where rnk <= 1000;