Skip to Main Content

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, santhoshreddy.

Asked: May 19, 2017 - 5:16 am UTC

Last updated: June 06, 2017 - 1:46 pm UTC

Version: 11G

Viewed 1000+ times

You Asked

Hi,
i read this below question
https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:948366252775
in latest follow up a reader posted like this
(
5 years later...

select * from X where rownum in (select 1 from dual)

still returns all the rows. 
)

--Query 1
select * from employees where rownum in (select 1 from dual)

So i tried that query it returning all rows. but select 1 from dual returns only one row with 1 value.

also i tried like this

--Query 2
select * from employees where rownum in (1,2,3); --Returns 3 rows

--Query 3
select * from employees where rownum in (1,2,3,5); --Returns 3 rows

--Query 4
select * from employees where rownum in (1,2,3,4); --Returns 4 rows


create table t2 (sno number);
inserted into t2 from 1-4 (refer live sql for all above scripts)

--Query 5
select * from employees where rownum in (select sno from t2);  --Returns 0 rows

so my questions are

1.Why query 1 returned all rows from employees table?
2.Query 2 returns 3 rows and query 3 also 3 rows but query 4 returns 4 why its returning like that?
3.Query 5 did not return any rows, sub query alos returns 1,2,3,4
4.How query works with IN Clause

with LiveSQL Test Case:

and Chris said...

Tom has a good explanation of how the rownum in query works and why this is wrong in the thread you've mentioned. But here's a brief recap:

The plan for this query looks like this:

SQL> set autotrace trace exp
SQL> select * from hr.employees where rownum in (select 1 from dual);

Execution Plan
----------------------------------------------------------
Plan hash value: 3351781302

---------------------------------------------------------------------------------
| Id  | Operation           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |           |   107 |  7383 |     5   (0)| 00:00:01 |
|   1 |  COUNT              |           |       |       |            |          |
|*  2 |   FILTER            |           |       |       |            |          |
|   3 |    TABLE ACCESS FULL| EMPLOYEES |   107 |  7383 |     3   (0)| 00:00:01 |
|   4 |    FAST DUAL        |           |     1 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter( EXISTS (SELECT 0 FROM "SYS"."DUAL" "DUAL" WHERE ROWNUM=1))


Notice that the IN has become:

EXISTS (SELECT 0 FROM "SYS"."DUAL" "DUAL" WHERE ROWNUM=1)


This will be true for every row. But it's also wrong! This has a different meaning to the original query. So you get all the rows instead of just one.

A query with:

col in (1,2,3)


Is equivalent to:

col = 1 or col = 2 or col = 3


So when you have:

rownum in (1,2,3,5)


What you really have is:

rownum = 1 or rownum = 2 or rownum = 3 or rownum = 5


This only returns three rows for the same reason

rownum = 2


returns nothing: you never have a row where rownum = 4. So rownum = 5 is always false. So you just get three rows. Whereas

rownum in (1,2,3,4)


will have a row where the rownum = 4, so you get 4 back.

Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Amol Patel, May 22, 2017 - 5:14 pm UTC

Hi,

Below both queries return 3 records. Could you please let me know why?

select * from hr.employees
where rownum in (select level from dual connect by level <= 5);

select * from hr.employees
where rownum in (select level from dual connect by level <= 7);


Chris Saxon
May 31, 2017 - 10:15 am UTC

The optimizer is transforming these into queries like:

SELECT *
FROM
    (
        SELECT DISTINCT
            level "LEVEL"
        FROM
            "SYS"."DUAL" "DUAL"
        CONNECT BY
            level <= 5
    ) "VW_NSO_1",
    "HR"."EMPLOYEES" "EMPLOYEES"
WHERE
    "VW_NSO_1"."LEVEL" = ROWNUM;


Select distinct in the subquery returns the rows "out of order" (or at least isn't guaranteed to return them "in order"):

SELECT DISTINCT
    level "LEVEL"
FROM
    "SYS"."DUAL" "DUAL"
CONNECT BY
    level <= 5;

LEVEL  
1      
2      
4      <====
5      <====
3


The third and fourth rows from this query have the values 4 & 5. Which don't equal the next value for rownum (3). So these aren't returned and you only get three rows...

Spooky

Racer I., May 23, 2017 - 10:56 am UTC

It gets weirder :

select * from sysp.employee
where rownum + X in (select level from dual connect by level <= 14);

X Rows
0 3
1 2
2 1
3 2
4 1
5 2
6 1
7 3
8 2
...

go fig :)




Chris Saxon
May 31, 2017 - 10:15 am UTC

I'm not sure what's going on here. What's X?

A reader, May 31, 2017 - 10:30 am UTC

X is column from sysp.employee table and added that x values to rownum in where clause
Chris Saxon
June 01, 2017 - 10:33 am UTC

And what values does it contain? How did you set it?

Meaning of X

Racer I., June 02, 2017 - 10:44 am UTC

Hi,

I got the given results by manually using the given X value

select * from sysp.employee where rownum + 0 in..
select * from sysp.employee where rownum + 1 in..
select * from sysp.employee where rownum + 2 in..
...

Added info

Racer I., June 02, 2017 - 11:00 am UTC

1. The properties of the table (emplyoee in this case) does not seem to matter. Just tried it with another. It just needs to contain some rows.
2. The resulting output is always the same row (repeated 1 to 3 times). Possibly the "first" in the segment?
3. The number of rows for each X is stable (across reruns, or different tables) although the sequence looks random, particulary the 3 at X=7
4. If X reaches the inner value (14 here) or greater the result is always 0 rows. Almost understandable ;)
Chris Saxon
June 02, 2017 - 2:37 pm UTC

So... X is a constant?

Show us your complete script, including where you set the value for X!

Script

Racer I., June 06, 2017 - 10:53 am UTC

Hi,

There was no script (I did it manually) but there is now :

create table test_table (ID NUMBER(2), text VARCHAR2(10));

insert into test_table values (1, 'T1');

insert into test_table values (2, 'T2');

insert into test_table values (3, 'T3');

insert into test_table values (4, 'T4');

commit;

-- some examples :
select * from test_table
where rownum + 0 in (select level from dual connect by level <= 14);

select * from test_table
where rownum + 3 in (select level from dual connect by level <= 14);

select * from test_table
where rownum + 7 in (select level from dual connect by level <= 14);

declare
vRows NUMBER;
begin
dbms_output.enable(null);
dbms_output.put_line('X |Rows');
for i IN 1..15 LOOP
select count(*) INTO vRows from test_table
where rownum + i in (select level from dual connect by level <= 14);
dbms_output.put_line(i || '|' || vRows);
END LOOP;
end;

Table dropped.
Table created.
1 row created.
1 row created.
1 row created.
1 row created.
Commit complete.

ID TEXT
---------- ---------------
1 T1
1 T1
1 T1

3 rows selected.

ID TEXT
---------- ---------------
1 T1
1 T1

2 rows selected.

ID TEXT
---------- ---------------
1 T1
1 T1
1 T1

3 rows selected.

PL/SQL procedure successfully completed.

X |Rows
0|3
1|2
2|1
3|2
4|1
5|2
6|1
7|3
8|2
9|1
10|2
11|1
12|2
13|1
14|0
15|0

Chris Saxon
June 06, 2017 - 1:46 pm UTC

Thanks for providing a script. This is the same problem Amol found above. The connect by level becomes a select distinct, which returns values "out-of-order". Leading to the results you see.