Everyone knows that rownum in inline views blocks many query transformations, for example pushing/pulling predicates, scalar subquery unnesting, etc, and many people use it for such purposes as a workaround to avoid unwanted transformations(or even CBO bugs).
If we pull the predicate “column_value = 3″ from the following query to higher level
[sourcecode language=”sql” highlight=””]
select *
from (select * from table(odcinumberlist(1,1,1,2,2,2,3,3,3)) order by 1)
where rownum <= 2
and column_value = 3;
COLUMN_VALUE
————
3
3
[/sourcecode]
we will get different results:
[sourcecode language=”sql” highlight=”8″]
select *
from (select *
from (select * from table(odcinumberlist(1,1,1,2,2,2,3,3,3)) order by 1)
where rownum <= 2
)
where column_value = 3;
no rows selected
[/sourcecode]
Doc ID 62340.1
But we recently encountered a bug with it: lateral view with ROWNUM returns wrong results in case of lateral view decorrelation.
Compare results of this query with and without no_decorrelation hint:
with t1(a) as (select * from table(odcinumberlist(1,3))) ,t2(b) as (select * from table(odcinumberlist(1,1,3,3))) ,t(id) as (select * from table(odcinumberlist(1,2,3))) select * from t, lateral(select/*+ no_decorrelate */ rownum rn from t1,t2 where t1.a=t2.b and t1.a = t.id )(+) order by 1,2; ID RN ---------- ---------- 1 1 1 2 2 3 1 3 2 |
with t1(a) as (select * from table(odcinumberlist(1,3))) ,t2(b) as (select * from table(odcinumberlist(1,1,3,3))) ,t(id) as (select * from table(odcinumberlist(1,2,3))) select * from t, lateral(select rownum rn from t1,t2 where t1.a=t2.b and t1.a = t.id )(+) order by 1,2; ID RN ---------- ---------- 1 1 1 2 2 3 3 3 4 |
Of course, we can draw conclusions even from these results: we can see that in case of decorrelation(query with hint) rownum was calculated before the join. But to be sure we can check optimizer’s trace 10053:
[sourcecode language=”sql”]
******* UNPARSED QUERY IS *******
SELECT VALUE(KOKBF$2) "ID", "VW_DCL_76980902"."RN" "RN"
FROM TABLE("ODCINUMBERLIST"(1, 2, 3)) "KOKBF$2",
(SELECT ROWNUM "RN_0", VALUE(KOKBF$0) "ITEM_3"
FROM TABLE("ODCINUMBERLIST"(1, 3)) "KOKBF$0",
TABLE("ODCINUMBERLIST"(1, 1, 3, 3)) "KOKBF$1"
WHERE VALUE(KOKBF$0) = VALUE(KOKBF$1)
) "VW_DCL_76980902"
WHERE "VW_DCL_76980902"."ITEM_3"(+) = VALUE(KOKBF$2)
ORDER BY VALUE(KOKBF$2), "VW_DCL_76980902"."RN"
*************************
[/sourcecode]
I’ll modify it a bit just to make it more readable:
we can see that
select * from t, lateral(select rownum rn from t1,t2 where t1.a=t2.b and t1.a = t.id)(+) order by 1,2;
was transformed to
select t.id, dcl.rn from t, (select rownum rn from t1,t2 where t1.a=t2.b) dcl where dcl.a(+) = t.id order by 1,2;
And it confirms that rownum was calculated on the different dataset (t1-t2 join) without join filter by table t.
I created SR with Severity 1 (SR #3-19117219271) more than a month ago, but unfortunately Oracle development doesn’t want to fix this bug and moreover they say that is not a bug. So I think this is a dangerous precedent and probably soon we will not be able to be sure in the calculation of rownum and old fixes…