A little fun with you all...

Ok, this will be a two parter - I'll introduce you to the frustration that is "answer my question".

Part 1 - the question and my response and their initial response.

So, first the question - written in to me as a letter to the editor regarding a column I wrote on using rownum:

Hi,

I have a question about rownum which is not mentioned.

Lets say that I want to extract a 3 records out of a result where the middle one is the query. For example I have records:

ID Value
1 Hello
2 Hi
3 Wow
4 Shrek
5 Shus
6 What?

And I need a query that by receiving ID, it will extract the record with the id and the previous one to that and the next one. For example for selecting ID=4, it will return records: 3,4,5.

Is there a way to do it in Oracle?

Seems straightforward, concise, clear - and a question I've received numerous times before.  Looks like someone trying to emulate an ISAM library - porting some code to Oracle.  So - I respond:

This presumes that ID is "unique"

select * 
from
( select *
from t
where id >= (select nvl(max(id),0)
from t
where id < :id)
order by id)
where rownum <= 3;

 


For example, if you copy all_Objects into a table T and add a unique/primary key constraint on object_id:

select * 
from ( select *
from t where object_id >=
(select nvl(max(object_id),0)
from t
where object_id < :id)
order by object_id)
where rownum <= 3
order by object_id

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 2 0 0
Fetch 2 0.00 0.00 0 5 0 3
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 7 0 3

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 61

Rows Row Source Operation
------- ---------------------------------------------------
3 COUNT STOPKEY (cr=7 pr=0 pw=0 time=333 us)
3 VIEW (cr=7 pr=0 pw=0 time=307 us)
3 TABLE ACCESS BY INDEX ROWID T (cr=7 pr=0 pw=0 time=270 us)
3 INDEX RANGE SCAN T_PK (cr=5 pr=0 pw=0 time=282 us)(object id 55414)
1 SORT AGGREGATE (cr=2 pr=0 pw=0 time=154 us)
1 FIRST ROW (cr=2 pr=0 pw=0 time=104 us)
1 INDEX RANGE SCAN (MIN/MAX) T_PK (cr=2 pr=0 pw=0 time=85 us)(object id 55414)


Basically, we start by finding the row in 'front' of the one we need in the middle - the select max(id) does that bit for us and then starting with that record - retrieve in sorted order that record and the next two (rownum <= 3). 


A logical extension of the article - pretty efficient approach and answers the question.  (note; I added nvl(max(id),0) - the NVL bit after the fact this morning.... so that it 'works' for the first record as well - presuming that ID is a whole number)


OR DOES IT?


Apparently, it does not - the response was:



Hello Thomas,

Thanks for answering me.

This doesn?t help me, ....


Anyone want to guess why this didn't help them? It can be summarized in a single sentence - which I'll publish later. 


Read More.. The Tom Kyte Blog