6

I am writing a very simple query for an Oracle DB (version 9).

Somehow I can get first 5 rows:

select * from cities where rownum <= 5

But skipping 5 rows returns an empty result:

select * from cities where rownum >= 5

Using:

  • Oracle SQL Developer
  • Oracle DB version 9

Why is the second query returning an empty result?

AntonIva
  • 597
  • 1
  • 6
  • 22
  • Have you tried searching? There are dozens of similar questions in SO. – Aleksej Dec 06 '16 at 14:28
  • Couldn't find any so far. Please let me know if you find the answer. Thanks – AntonIva Dec 06 '16 at 14:29
  • 5
    Possible duplicate of [select rownum from salary where rownum=3;](http://stackoverflow.com/questions/3457370/select-rownum-from-salary-where-rownum-3) – Jiri Tousek Dec 06 '16 at 14:29
  • Newer Oracle versions support ANSI SQL's `FETCH FIRST`, but I don't know from which version... Try adding `FETCH FIRST 5 ROWS ONLY` at the end. Combine with `ORDER BY`! – jarlh Dec 06 '16 at 15:04
  • 1
    Possible duplicate of [Best practice for pagination in Oracle?](https://stackoverflow.com/questions/13738181/best-practice-for-pagination-in-oracle) – Ratan Uday Kumar Jun 05 '18 at 04:45

7 Answers7

13

In Oracle Database 12c (release 1) and above, you can do this very simple, for skip 5 rows:

SELECT * FROM T OFFSET 5 ROWS

and for skip 5 rows and take 15 rows:

SELECT * FROM T OFFSET 5 ROWS FETCH NEXT 15 ROWS ONLY
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Milad Aghamohammadi
  • 1,866
  • 1
  • 16
  • 29
4

Oracle increments rownum each time it adds a row to the result set. So saying rownum < 5 is fine; as it adds each of the first 5 rows it increments rownum, but then once ruwnum = 5 the WHERE clause stops matching, no more rows are added to the result, and though you don't notice this rownum stops incrementing.

But if you say WHERE rownum > 5 then right off the bat, the WHERE clause doesn't match; and since, say, the first row isn't added to the result set, rownum isn't incremented... so rownum can never reach a value greater than 5 and the WHERE clause can never match.

To get the result you want, you can use row_number() over() in a subquery, like

select *
  from (select row_number() over() rn, -- other values
          from table
         where -- ...)
 where rn > 5

Update - As noted by others, this kind of query only makes sense if you can control the order of the row numbering, so you should really use row_number() over(order bysomething) where something is a useful ordering key in deciding which records are "the first 5 records".

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • To apply ordering, use `over(order by MyColumn)` – JohnHC Dec 06 '16 at 14:34
  • Thanks, Mark. Do you know why this query doesn't work `select * from (select row_number() over (order by id) rn, c.* from cities c ) where rn > 3` ? Error: `%s: invalid identifier` – AntonIva Dec 06 '16 at 14:40
  • @AntonIva: "doesn't work" as in "is invalid SQL"? Or "aborts execution with an error"? Or "returns different result than I expected" (if so what did you expect)? – Codo Dec 06 '16 at 14:43
  • Error as this: https://www.dropbox.com/s/gdpodlfbu9l7q8m/Screenshot%202016-12-06%2009.47.44.png?dl=0 What type of error is this? @Codo – AntonIva Dec 06 '16 at 14:48
  • 1
    In the screenshot, it appears you left off the `rn` in `row_number() over(order by id) rn` – Mark Adelsberger Dec 06 '16 at 15:20
4

You can use the following query to skip the first not n of rows.

select * from (
 select rslts.*, rownum as rec_no from (
  <<Query with proper order by (If you don't have proper order by you will see weird results)>>
 ) rslts
) where  rec_no > <<startRowNum - n>>

The above query is similar to pagination query below.

select * from (
 select rslts.*, rownum as rec_no from (
  <<Query with proper order by (If you don't have proper order by you will see weird results)>>
 ) rslts where  rownum <= <<endRowNum>>
) where  rec_no > <<startRowNum>>

Your cities query:

select * from (
 select rslts.*, rownum as rec_no from (
  select * from cities order by 1
 ) rslts
) where  rec_no > 5 <<startRowNum>>

Note: Assume first column in cities table is unique key

Joshan George
  • 668
  • 1
  • 7
  • 14
1

rownum is being increased only when a row is being output, so this type of condition won't work.
In any case, you are not ordering your rows, so what's the point?

David דודו Markovitz
  • 42,900
  • 6
  • 64
  • 88
1

Used row_number() over (order by id):

select * from
(select row_number() over (order by id) rn, c.* from countries c)
where rn > 5

Used ROWNUM:

select * from
(select rownum rn, c.* from countries c)
where rn > 5

Important note:

Using alias as countries c instead of countries is required! Without, it gives an error "missing expression"

AntonIva
  • 597
  • 1
  • 6
  • 22
  • The first query doesn't work because it doesn't name the `row_number() over(...)` expression to `rn` so `rn` is undefined. Because `row_number() over()` is standard whereas `rownum` is Oracle-specific, I recommend using the former. – Mark Adelsberger Dec 06 '16 at 15:22
  • Sorry, yes I missed. Thanks, @Mark. Fixed. But it still gives an error: https://www.dropbox.com/s/k1uetavxft8l0jl/Screenshot%202016-12-06%2011.01.31.png?dl=0 – AntonIva Dec 06 '16 at 16:04
  • Does the table have an `ID` column? That's the identifier it's complaining about... – Mark Adelsberger Dec 06 '16 at 16:13
  • Thank you very much, @MarkAdelsberger. Yes, you are right. Fixed the answer too. – AntonIva Dec 06 '16 at 20:40
  • You can always type out the name of the table/view rather than using an alias. So for your example above, just type out 'countries', as in: SELECT * FROM (SELECT ROWNUM rn, countries.* FROM countries) WHERE rn > 5 – Brian Leach Dec 07 '16 at 00:19
1

Even better would be:

select * from mytab sample(5) fetch next 1 rows only;

Sample clause indicates the probability of each row getting picked up in the sampling process. FETCH NEXT clause indicates the number of rows you want to select.

Srikrishnan Suresh
  • 729
  • 2
  • 13
  • 31
0

With this code, you can query your table with skip and take.

select * from (
 select a.*, rownum rnum from (
  select * from cities
 ) a
) WHERE rnum >= :skip + 1 AND rnum <= :skip + :take

This code works with Oracle 11g. With Oracle 12, there is already a better way to perform this queries with offset and fetch

Pedro Silva
  • 2,655
  • 1
  • 15
  • 24