Performance difference between left outer join / inner join
user9038Jan 12 2009 — edited Jan 12 2009Hi,
I've got a complex query which among other things accesses quite a large table. If I use inner join to join this table, the response is quite fast and execution plan shows it uses nested loops to gather the data.
If I change inner join to left outer join, I get a big performance drop. Cost of query goes from 1441 to 28544.
I don't uderstand why there's such a difference. For inner join, database has to remove all the rows that don't have match in inner-joined table. For left join it can keep all records and just return NULL values for records that don't have a match. In my mind left join should be faster, as it seems simpler. And the access plan could be the same, couldn't it?
Execution plan for inner join:
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 288 | 1441 (1)| 00:00:18 |
| 1 | HASH GROUP BY | | 1 | 288 | 1441 (1)| 00:00:18 |
| 2 | NESTED LOOPS OUTER | | 1 | 288 | 1440 (1)| 00:00:18 |
| 3 | NESTED LOOPS | | 1 | 261 | 1438 (1)| 00:00:18 |
| 4 | NESTED LOOPS | | 318 | 74412 | 508 (1)| 00:00:07 |
| 5 | NESTED LOOPS | | 318 | 51834 | 189 (0)| 00:00:03 |
| 6 | TABLE ACCESS BY INDEX ROWID| RESURCE | 1 | 106 | 1 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | RESURCE_PRINCIPAL_NAME_INDEX | 1 | | 0 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID| TASK_USES_RESURCE | 318 | 18126 | 188 (0)| 00:00:03 |
|* 9 | INDEX RANGE SCAN | TASK_USES_RESUR_IDX$$_0CDC0002 | 318 | | 3 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TASK | 1 | 71 | 1 (0)| 00:00:01 |
|* 11 | INDEX UNIQUE SCAN | TASK_PK | 1 | | 0 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID | TASK_WORK_HISTORY | 1 | 27 | 3 (0)| 00:00:01 |
|* 13 | INDEX RANGE SCAN | TASK_WORK_HISTORY_INDEX1 | 1 | | 2 (0)| 00:00:01 |
| 14 | TABLE ACCESS BY INDEX ROWID | TASK_USES_RESURCE | 1 | 27 | 2 (0)| 00:00:01 |
|* 15 | INDEX UNIQUE SCAN | TASK_USES_RESURCE_UK1 | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------------
For left outer join:
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 318 | 1596K| | 28544 (2)| 00:05:43 |
|* 1 | HASH JOIN OUTER | | 318 | 1596K| 1584K| 28544 (2)| 00:05:43 |
| 2 | VIEW | | 318 | 1580K| | 508 (1)| 00:00:07 |
| 3 | NESTED LOOPS | | 318 | 74412 | | 508 (1)| 00:00:07 |
| 4 | NESTED LOOPS | | 318 | 51834 | | 189 (0)| 00:00:03 |
| 5 | TABLE ACCESS BY INDEX ROWID| RESURCE | 1 | 106 | | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | RESURCE_PRINCIPAL_NAME_INDEX | 1 | | | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| TASK_USES_RESURCE | 318 | 18126 | | 188 (0)| 00:00:03 |
|* 8 | INDEX RANGE SCAN | TASK_USES_RESUR_IDX$$_0CDC0002 | 318 | | | 3 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID | TASK | 1 | 71 | | 1 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | TASK_PK | 1 | | | 0 (0)| 00:00:01 |
| 11 | VIEW | | 1480K| 73M| | 23431 (2)| 00:04:42 |
|* 12 | HASH JOIN RIGHT OUTER | | 1480K| 76M| 38M| 23431 (2)| 00:04:42 |
| 13 | TABLE ACCESS FULL | TASK_USES_RESURCE | 1486K| 21M| | 2938 (2)| 00:00:36 |
| 14 | VIEW | | 1445K| 53M| | 15031 (2)| 00:03:01 |
| 15 | HASH GROUP BY | | 1445K| 37M| 110M| 15031 (2)| 00:03:01 |
| 16 | TABLE ACCESS FULL | TASK_WORK_HISTORY | 1445K| 37M| | 3897 (2)| 00:00:47 |
--------------------------------------------------------------------------------------------------------------------------