Rownum not working properly [duplicate] - oracle

This question already has answers here:
How to use Oracle ORDER BY and ROWNUM correctly?
(5 answers)
Closed 4 years ago.
I have two tables X and Y.
when I run below query for table X
select * from
( select rownum as rn, A.* from X as A order by A.IDdesc) where rn between 0
and 1000000;
I get result as
rn Id name
1 1 xxx
2 2 yyy
3 4 zzz
but for Y table when same query is executed I get result as
select * from
( select rownum as rn, A.* from Y as A order by A.IDdesc) where rn between 0
and 1000000;
rn Id name
5 1 xxx
7 2 yyy
10 4 zzz
rn in Y table is getting some random numbers. Not able to understand this behavior. ny help would be appreciated.

The ROWNUM pseudo-column is added to the selected columns in the order Oracle retrieves the rows and then the ORDER BY clause is applied. What you appear to want is to order the rows first and then generate the ROWNUM against this ordering but that is not what your query will do.
For table X, it happens that Oracle retrieves the rows in the same order as the id column. However, for table Y, the rows are not retrieved in the same order as the id column so the ROWNUM is in the order the rows were retrieved and then a different ordering is applied so the ROWNUM values appear shuffled.
What you should do is to apply the ORDER BY before generating the ROWNUM pseudo-column so they are in-sync:
SELECT *
FROM (
SELECT rownum as rn,
A.*
FROM (
SELECT *
FROM X
ORDER BY ID desc
) A
)
WHERE rn BETWEEN 0 AND 1000000;

Related

How to find last 500 records from a table in sql with out using union,union all or minus function in sql.? [duplicate]

This question already has answers here:
Oracle SELECT TOP 10 records [duplicate]
(6 answers)
Closed 2 years ago.
Is there any way to find the last 500 records from a table with out using union, union all or minus function?
Can we achieve this using rank, row_number or dense_rank functions in sql.
Thanks
Rakesh
Teradata uses TOP
SELECT TOP 500 * FROM table ORDER BY your_column
Oracle 12c+ uses FETCH:
SELECT * FROM TABLE ORDER BY your_column DESC FETCH FIRST 500 ROWS ONLY
Older oracle uses rownum, and the orderby must be done in a subquery:
SELECT * FROM (SELECT * FROM TABLE ORDER BY your_column DESC) WHERE rownum <= 500
You could use ROW_NUMBER in a DB that supports it:
SELECT * FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY your_column DESC) rn FROM TABLE) WHERE rn <= 500
your_column is used to determine "last"ness.. It needs to be something that sorts sensibly, like a numeric id, date etc
Edit:
Your interviewer expected you to use analytical functions. Here's what it would look like:
SELECT *
FROM
(
SELECT *, ROW_NUMBER() OVER(ORDER BY your_column DESC) as rn
FROM table
) x
WHERE x.rn < 501
Not that it still needs an order by; here's what happens when you skip it:
You can use the SQL limit with orderby desc to get the last N number of records.
select * from tbl_name order by id desc limit N;

Row num is not working for duplicate columns while pagination

Code is written below, multiple nested query:
select *
from
(select employee_id,employee_id
from employees) a
where rownum <= 5
)
where rnum >= 10
If i give duplicate columns in the select its giving "column ambiguously defined" error.
employee_id,employee_id, rownum rnum
Firstly, your query is incorrect since you have ambiguously defined the columns. It will throw ORA-00918: column ambiguously defined.
You must use proper ALIAS to avoid the error. For example,
SELECT departments.department_id AS "dept_id",
employees.department_id AS "emp_Dept_id"
FROM...
Secondly, it is not at all a pagination query. Since you are alwyas going to pick random rows as there is no ORDER BY clause. You are not ordering the rows.
where rownum <= 5
where rnum >= 10
At last, how on earth could you try to fetch the rows beyond 10 when you have fetched ONLY 5 rows in the inner query? It will ALWAYS return zero rows.
The correct way of paging through data is:
SQL> SELECT empno
2 FROM (SELECT empno, rownum AS rnum
3 FROM (SELECT empno
4 FROM emp
5 ORDER BY sal)
6 WHERE rownum <= 8)
7 WHERE rnum >= 5;
EMPNO
----------
7654
7934
7844
7499
SQL>
when you give a.* it means you are trying to refer two columns with same name in a table which is not permitted.The column names in a table are unique
So select employee_id,employee_id from employees is not a problem
but
select a.* from (select employee_id,employee_id from employees)a is a problem
the sqlfiddle here
Also if you want records from 10 to 15 in your query then use like the below
select * from
(select a.*,Rownum rnum from
(select employee_id as emp_id1,employee_id as emp_id2
from employees order by 1)
a where rownum <= 15 ) where rnum >= 10
EDIT1:- If duplicate column is required use like the below
with emp1 as (select employee_id from employees),
emp2 as (select * from
(select a.*,rownum rnum from emp1 a order by 1)
where rownum <=15)
select b.*,c.*
from emp1 b,emp2 c
where b.employee_id=c.employee_id
and c.rnum >=10

Fetching Lastrow from the table in toad

I have to fetch the first and last row of the table in Toad.
I have used the following query
select * from grade_master where rownum=(select max(rownum) from grade_master)
select * from grade_master where rownum=1
The second query works to fetch the first row. but the first not working. Anyone please help me.
Thanks in advance
Such request makes sense if you specify sort order of the results - there are no such things in database as "first" and "last" rows if sort order is not specified.
SQL> with t as (
2 select 'X' a, 1 b from dual union all
3 select 'C' , 2 from dual union all
4 select 'A' a, 3 b from dual
5 )
6 select a, b, decode(rn, 1, 'First','Last')
7 from (
8 select a, b, row_number() over(order by a) rn,
9 count(*) over() cn
10 from t
11 )
12 where rn in (1, cn)
13 order by rn
14 /
A B DECOD
- ---------- -----
A 3 First
X 1 Last
In oracle the data is not ordered until you specify the order in you sql statement.
So when you do:
select * from grade_master
oracle will give the rows in anyway it want wants.
OTOH if you do
select * from grade_master order by id desc
Then oracle will give the rows back ordered by id descending.
So to get the last row you could do this:
select *
from (select * from grade_master order by id desc)
where rownum = 1
The rownum is determined BEFORE the "order by" clause is assessed, so what this query is doing is ordering the rows descending (the inside query) and then giving this ordered set to the outer query. The outer gets the first row of the set then returns it.

Using rownum with the combination of between keyword

I have seen this Oracle SQL query for using rownum with the combination of between keyword .
select *
from
( select rownum rnum, a.*
from (your_query) a
where rownum <= :M )
where rnum >= :N;
in order to get rows n through m from 'your query.'
I want to try it , Could anybody please tell me how can i get the data from an Emp table to fetch records from 4 to 8 Records
select *
from
( select rownum rnum, a.*
from (select * from emp) a
where rownum <= 4 )
where rnum >= 8;
But this isn't working , could anybody please tell me why .
Thank you very much .
This is because you are limiting your query to <=4 rows, so when you filter to show records >=8 there are only 4 records to look at....
Invert the numbers and you should see a result:
select *
from
( select rownum rnum, a.*
from (select * from emp) a
where rownum <= 8 )
where rnum >= 4;
If I had to guess, I'd say that the reason you're not seeing what you expect (in addition to having the operators backwards, as pointed out by #diagonalbatman) is that you didn't tell the database what order you wanted the rows in. You're essentially telling the database to return any 5 rows. You can't even be sure that this query will always return the same five rows. Any time you're getting a subset like this, you should use an order by clause in the innermost query, so that the sort is applied before the rownum values are issued:
SELECT *
FROM (SELECT ROWNUM rnum, a.*
FROM (SELECT *
FROM emp
ORDER BY emp_id) a
WHERE ROWNUM <= 8)
WHERE rnum >= 4;
When you are querying the whole data in the inner statement (if you have huge data amounds, no good idea!) you could as well use the BETWEEN keyword.
SELECT *
FROM
(SELECT rownum AS rnum,
a.*
FROM EMP) a
WHERE rnum BETWEEN 4 AND 8;

Issue in SQL query full scanning twice?

Table A
ID EmpNo Grade
--------------------
1 100 HIGH
2 105 LOW
3 100 MEDIUM
4 100 LOW
5 105 LOW
Query:
select *
from A
where EMPNO = 100
and rownum <= 2
order by ID desc
I tried this query to retrieve max and max-1 value; I need to compare the grade from max and max-1, if equals I need to set the flag as 'Y' or 'N' without using a cursor. Also I don't want to scan the entire record twice.
Please help me.
ROWNUM is applied before ORDER BY, so you need to nest the query like this:
select * from
(select * from A where EMPNO =100 order by ID desc)
where rownum<=2
That only performs one table scan (or it may use an index on EMPNO).
select *
from (
select id, emp_no, grade
, case
when lag(grade) over (order by emp_no desc) = grade
then 'Y'
else 'N'
end
as flag
, dense_rank() over( order by emp_no desc) as rank
from t
)
where rank <=2
;

Resources