Pagination issue with ROWNUM [duplicate] - sql

I am struggling to fetch the data based on rownum. When I execute the below query to get the results based rownum 1 to 4 then it is working fine.
SELECT ROWNUM TOTAL,MI.* FROM (SELECT USER_ID,CUSTOMER_NAME FROM ELEC_AUTO_MERC
ORDER BY CREATION_DATE DESC ) MI WHERE ROWNUM BETWEEN 1 AND 4;
But when I am executing same query to get result from rownum 2 to 4 then it is not working, it doesn't return anything.
SELECT ROWNUM TOTAL,MI.* FROM (SELECT USER_ID,CUSTOMER_NAME FROM ELEC_AUTO_MERC
ORDER BY CREATION_DATE DESC ) MI WHERE ROWNUM BETWEEN 2 AND 4;
As a workaround, when I use one more SELECT statement then it is working fine, but I don't think it is good approach to use SELECT multiple times only for rownum.
SELECT * FROM (SELECT ROWNUM TOTAL,MI.* FROM (SELECT USER_ID,CUSTOMER_NAME FROM ELEC_AUTO_MERC
ORDER BY CREATION_DATE DESC ) MI) WHERE TOTAL BETWEEN 2 AND 4;
Can you please help me out to create optimize query?

ROWNUM is weird in that it can be evaluated as part of a condition in the query - but if the row then fails to pass that filter, the ROWNUM value that it was assigned becomes available to be used again for the next row.
One important effect of this is that if you use any condition that excludes a ROWNUM value of 1, you will never get a match. The first row to be tested against this condition will be row 1; but then it will fail the test, so the next row will then be considered row 1; and so on.
So your condition ROWNUM BETWEEN 2 AND 4 can never be true.
The workaround you have found is the traditional one. Another would be to use an analytic function to rank the rows, then filter on the rank, e.g.:
SELECT MI.* FROM (
SELECT USER_ID,CUSTOMER_NAME, RANK() OVER (ORDER BY CREATION_DATE DESC) AS the_rank
FROM ELEC_AUTO_MERC
) MI
WHERE the_rank BETWEEN 2 AND 4;
Several analytic functions - RANK, DENSE_RANK, and ROW_NUMBER - can be used for this purpose, and will produce slightly different results, especially if there are ties. Check out the docs.

Related

using of rownum function with ">" sign in oracle [duplicate]

This question already has answers here:
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 5 years ago.
I want to select the number of rows which are greater than 3 by rownum function i_e "(rownum>3)"
for example if there are 25 rows and I want to retrieve the last 22 rows by rownum function.
but when I write the
select * from test_table where rownum>3;
it retrieve no row.
can any one help me to solve this problem.
thanks in advance
In RDBMS there is no first or last rows. What you calls "raws" , actually is set(sets), they can be ordered or not. rownum is a function, which is just enumerates result set, it makes sense only after set is calculated, to order your set of data (rows) you should do it in your query before rownum call, you must tell DB what means for the order in particular select statement.
It is not working because: for the first row assumes the ROWNUM of 1 and since your WHERE clause is ROWNUM>3 then this reduces to 1>3 and the row is discarded. The subsequent row will then be tested against a ROWNUM of 1 (since the previous row is no longer in the output and now does not require a row number), which will again fail the test and be discarded. Repeat, ad nauseum and all rows fail the WHERE clause filter and are discarded.
If you want to assign the rows a ROWNUM then you need to do this is a sub-query:
SELECT * -- Finally, in the outer query, filter on the assigned ROWNUM
FROM (
SELECT t.*, -- First, in the inner sub-query, apply a ROWNUM
ROWNUM AS rn
FROM test_table t
)
WHERE rn > 3;
Or, if you want to order the results before numbering:
SELECT * -- Finally, in the outer query, filter on the assigned ROWNUM
FROM (
SELECT t.*, -- Second, in the next level sub-query, apply a ROWNUM
ROWNUM AS rn
FROM (
SELECT * -- First, in the inner-most sub-query, apply an order
FROM test_table
ORDER BY some_column
) t
)
WHERE rn > 3;
select * from (select rownum as rn, t.* from test_table t) where rn > 3
see this article for more samples
On Top-n and Pagination Queries By Tom Kyte

How to skip/offset rows in Oracle database?

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?
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
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
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".
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?
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"
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.
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

How to select a particular row of query result

I have a following table in my Oracle database:
How can I select the course which is done by most of the students?
I am trying multiple variations in following SQL query but it's not working -
select count(course) as pcourse, course
from studies
group by course
order by pcourse dec;
You need to use rownum, which is Oracle's equivalent of TOP 1 (from SQL Server) or LIMIT 1 (from MySQL).
select *
from
(select count(course) as pcourse
, course
from studies
group by course
order by pcourse desc) result_set
where rownum = 1
It is important that you use where rownum = 1 in a higher level SELECT because Oracle requires you to first sort the result set and then it will filter the rows.
Otherwise, if you would include the where rownum = 1 condition in the same level as the SELECT with which you get your data, then it will be immediately be filtering to 1 row (the first it finds) and sorting it (which won't make any sense, since it will only be one row).
As per #a_horse_with_no_name's comment, apparently Oracle gives you the possibility to write a TOP 1 filtering condition in the same level SELECT, like below:
select count(course) as pcourse
, course
from studies
group by course
order by pcourse desc
fetch first 1 rows only;
This should work but I haven't created your test data because it is in a picture, please include as text next time
SELECT
course
,COUNT(*) AS pcourse
FROM
Studies
GROUP BY
course
ORDER BY
COUNT(*) DESC
FETCH FIRST ROW ONLY
I am still a little unsure of how to the fetch first row/select top 1/limit 1 for Oracle but #a-horse_with_no_name is teaching and I am learning so here is stab at it. here is a link for Oracles syntax on offest and fetch http://docs.oracle.com/javadb/10.5.3.0/ref/rrefsqljoffsetfetch.html
Since this is an Oracle DB, i think the following should work
SELECT * FROM
(select count(course) as pcourse, course
from studies
group by course
order by pcourse dec)
WHERE ROWNUM <= 1
Reply to the Comment
That's because rownum is assigned sequentially to the rows that are returned.
So let's say you put ROWNUM = 2, your query will return the first row, and temporarily give it rownum of 1. But since it does not match the condition ROWNUM = 2, it will be discarded. Then it will go on to fetch the second row, and give it also rownum 1, and the cycle continues.
If you want to get the Nth row, you could probably do this
SELECT pcourse, course FROM
(select count(course) as pcourse, course, rownum as rn
from studies
group by course
order by pcourse dec)
WHERE rn = N
where N is the required row.

row num is not displaying any rows when using between keyword [duplicate]

This question already has answers here:
How ROWNUM works in pagination query?
(3 answers)
Closed 3 years ago.
When I am using rownum and between keywords then the query doesn't return any rows. Can anyone explain the reason why query is not retrieving any rows?
select * from cus where rownum between 2 and 6;
I just want to check whether rownum will work when it is used with between keyword .So ,I just tried the above query to display the rows which are in between 2 and 6. But when I tried to execute the query, it doesn't retrieve any rows.
thanks in advance
Oracle rownum starts at 1, so you will never get the first rownum if you say between 2 and N.
It takes a row to "initiate" the rownum pseudocolumn sequence, so by eliminating rownum 1 in your criteria, you eliminate all rownums (or every row essentially has rownum 0).
Look at it like this. You don't get a ROWNUM until the database returns a row to you. The first row of any criteria will always be ROWNUM 1.
Now, the trick you can use is to use a subquery. Each subquery will have its own rownum, and if you alias it to another column name, you can preserve it into outer queries, and treat it however you like. So if you are looking to implement paging of a result set, you would normally alias rownum from inner results as rownum_ to an outer subquery to limit with BETWEEN.
select * from
(select t.*, rownum as rownum_ from t)
where rownum_ between 2 and 6
But note, that the outer result set will have its own rownum, so you could do:
select t2.*, rownum from
(select a, b, rownum as rownum_ from t) t2
where rownum_ between 2 and 6
You will see rownum on the final result still starts at 1, but your inner result will have rownum_ starting at 2.
select * from cus where rownum between 2 and 6;
That is completely wrong. Because, ROWNUM is a pseudo-column which increments to 2 only when it started at ROW one(random, of course). In your query, the predicate ROWNUM BETWEEN 2 AND 6 is meaningless, since ROWNUM has not yet been assigned.
Let's understand this step-by-step :
Understand how a SQL statement is interpreted. After the query is parsed, the filter is applied.
In your query, the filter ROWNUM BETWEEN 2 AND 6 is meaningless, since, Oracle has not yet assigned ROWNUM 1 as the first row is
not yet fetched.
When the first row is fetched, then ROWNUM is assigned as a pseudo-number. But the filter in your query directly points to rows
between 2 and 6, which is absurd. So, no rows are returned.
Mysql doesnt have rownum.
If you are looking for oracle maybe you can try something like this:
select * from (select cus.*, rownum as row_num from cus)
where row_num between 2 and 6;
or
select * from (select cus.*, rownum as row_num from cus)
where row_num >1 and row_num <=6;

Oracle Select query help please

SELECT id
FROM (
SELECT id
FROM table
WHERE
PROCS_DT is null
ORDER BY prty desc, cret_dt ) where rownum >0 and rownum <=100
The above query is giving me back 100 records as expected
SELECT id
FROM (
SELECT id
FROM table
WHERE
PROCS_DT is null
ORDER BY prty desc, cret_dt ) where rownum >101 and rownum <=200
why is the above query returning me zero records?
Can some one help me how i can keep on. I am dumb in oracle...
Try this:
SELECT id
FROM
(SELECT id,
rownum AS rn
FROM
(SELECT id
FROM TABLE
WHERE PROCS_DT IS NULL
ORDER BY prty DESC, cret_dt) )
WHERE rn >101
AND rn <=200
If you are comfortable using the ANALYTIC functions, try this:
SELECT id
FROM
(
SELECT id,
ROW_NUMBER() OVER(ORDER BY prty DESC, cret_dt ) rn
FROM table
WHERE procs_dt IS NULL
)
WHERE rn >101 and rn <=200
ROWNUM values are assigned to rows as they are returned from a query (or subquery). If a row is not returned, it is not assigned a ROWNUM value at all; so the ROWNUM values returned always begin at 1 and increment by 1 for each row.
(Note that these values are assigned prior to any sorting indicated by the ORDER BY clause. This is why in your case you need to check rownum outside the subquery.)
The odd bit of logic you have to understand is that when you have a predicate on ROWNUM, you are filtering on a value that will only exist if the row passes the filter. Conceptually, Oracle applies any other filters in the query first, then tentatively assigns ROWNUM 1 to the first matching row and checks it against the filter on ROWNUM. If it passes this check, it will be returned with that ROWNUM value, and the next row will be tentatively assigned ROWNUM 2. But if it does not pass the check, the row is discarded, and the same ROWNUM value is tentatively assigned to the next row.
Therefore, if the filter on ROWNUM does not accept a value of 1, no rows will ever pass the filter.
The use of the analytic function ROW_NUMBER() shown in the other answers is one way around this. This function explicitly assigns row numbers (distinct from ROWNUM) based on a given ordering. However, this can change performance significantly, as the optimizer does not necessarily realize that it can avoid assigning numbers to ever possible row in order to complete the query.
The traditional ROWNUM-based way of doing what you want is:
SELECT id
FROM (
SELECT rownum rn, id
FROM (
SELECT id
FROM table
WHERE
PROCS_DT is null
ORDER BY prty desc, cret_dt
) where rownum <=200
) where rn > 101
The innermost query conceptually finds all matching rows and sorts them. The next layer assigns ROWNUMs to these and returns only the first 200 matches. (And actually, the Oracle optimizer understands the significance of a sort followed by a ROWNUM filter, and will usually do the sort in such a way as to identify the top 200 rows without caring about the specific ordering of the other rows.)
The middle layer also takes the ROWNUMs that it assigns and returns them as part of its result set with the alias "rn". This allows the outermost layer to filter on that value to establish the lower limit.
I would experiment with this variant and the analytic function to see which performs better in your case.