Skip to Main Content

SQL & PL/SQL

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

query transformation with left join to lateral view

James Su4 days ago

hi experts, here's a test case on 19c (19.28):


drop table t1;

create table t1 as select * from all_objects;

create index t1x1 on t1(owner);

drop table t2;

create table t2 as select * from all_objects;

create index t2x on t2(object_id);


select t1.owner,t1.object_id,t1.object_type,t1.object_name,v2.cnt
  from t1
       LEFT join lateral (
         select t2.*
               ,count(*) over(partition by t2.owner) cnt
           from t2
          where t2.object_id=t1.object_id
       ) v2 on t1.object_id=v2.object_id
where t1.owner='SJAMES';

Plan hash value: 3054039542
 
------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                 |  3337 |   211K| 10164  (33)| 00:00:01 |
|   1 |  MERGE JOIN OUTER                        |                 |  3337 |   211K| 10164  (33)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED    | T1              |  3337 |   169K|   151   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                      | T1X1            |  3337 |       |    12   (0)| 00:00:01 |
|   4 |   BUFFER SORT                            |                 |     1 |    13 | 10013  (34)| 00:00:01 |
|   5 |    VIEW                                  | VW_LAT_D4FD8C38 |     1 |    13 |     3  (34)| 00:00:01 |
|*  6 |     VIEW                                 | VW_LAT_A18161FF |     1 |    26 |     3  (34)| 00:00:01 |
|   7 |      WINDOW SORT                         |                 |     1 |    16 |     3  (34)| 00:00:01 |
|   8 |       TABLE ACCESS BY INDEX ROWID BATCHED| T2              |     1 |    16 |     2   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN                  | T2X             |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("T1"."OWNER"='SJAMES')
   6 - filter("T1"."OBJECT_ID"="V2"."OBJECT_ID")
   9 - access("T2"."OBJECT_ID"="T1"."OBJECT_ID")

Why are there two lateral views in the sql plan? On our production, this plan is very slow, much slower than select count(*) within a scalar subquery.

If I change it to inner join, then there's only lateral view and it's a lot faster. Unfortunately I need outer join in our use case, and the code would look better without an extra scalar subquery.

select t1.owner,t1.object_id,t1.object_type,t1.object_name,v2.cnt
  from t1
       INNER join lateral (
         select t2.*
               ,count(*) over(partition by t2.owner) cnt
           from t2
          where t2.object_id=t1.object_id
       ) v2 on t1.object_id=v2.object_id
where t1.owner='SJAMES';


Plan hash value: 3712667785
 
----------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                 |     1 |    78 | 10164  (33)| 00:00:01 |
|   1 |  NESTED LOOPS                          |                 |     1 |    78 | 10164  (33)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | T1              |  3337 |   169K|   151   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                    | T1X1            |  3337 |       |    12   (0)| 00:00:01 |
|*  4 |   VIEW                                 | VW_LAT_535DE542 |     1 |    26 |     3  (34)| 00:00:01 |
|   5 |    WINDOW SORT                         |                 |     1 |    16 |     3  (34)| 00:00:01 |
|   6 |     TABLE ACCESS BY INDEX ROWID BATCHED| T2              |     1 |    16 |     2   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN                  | T2X             |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("T1"."OWNER"='SJAMES')
   4 - filter("T1"."OBJECT_ID"="V2"."OBJECT_ID")
   7 - access("T2"."OBJECT_ID"="T1"."OBJECT_ID")   
This post has been answered by Solomon Yakobson on Nov 17 2025
Jump to Answer
Comments
Post Details
Added 4 days ago
8 comments
104 views