You should take those general advice Index is good, Full Table Scan is bad! with with some care.
It always depends from the context of the usage.
I reformulate your example removing the not relevant parts and using neutral column names
I also added alias to qualify the columns, so it is clear from which table the columns are taken.
SELECT a.id, a.col1, a.col2, a.col3,
b.col4
FROM a
LEFT OUTER JOIN b
ON a.id = b.fk_id AND
b.col1 = 8 AND
b.col2 IS NULL AND
b.col3 IS NULL
Note, that in the join you selects all rows from the tables A - which means no index for access on table A will help. You have to full scan the table and process all rows.
You will see a HASH JOIN
as a used join operation which is the prefered option if you join large datasets.
Check here how you can get the execution plan of the query.
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 1044K| 25 (4)| 00:00:01 |
|* 1 | HASH JOIN RIGHT OUTER| | 10000 | 1044K| 25 (4)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | B | 2 | 110 | 16 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | A | 10000 | 507K| 8 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."ID"="B"."FK_ID"(+))
2 - filter("B"."COL3"(+) IS NULL AND "B"."COL2"(+) IS NULL AND
"B"."COL1"(+)=8)
A different situation will be if you limit the processed rows from the table A
to a very small number, say using the same query with additional WHERE
condition.
You will use a nested loop join and index access to get only the required rows.
SELECT a.id, a.col1, a.col2, a.col3,
b.col4
FROM a
LEFT OUTER JOIN b
ON a.id = b.fk_id AND
b.col1 = 8 AND
b.col2 IS NULL AND
b.col3 IS NULL
where a.id = 8 --<<<< here you select only a few rows
Now you can profit from defining indices for
- access to table
A
with the WHERE
predicate
- access to table
B
with the join
columns
In our case it will be
create index a_idx on a (id);
create index b_idx on b (fk_id,col1, col2, col3);
The execution plan that you should expect will be the nested loop outer join
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 214 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 2 | 214 | 4 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| A | 1 | 52 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | A_IDX | 1 | | 1 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| B | 2 | 110 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | B_IDX | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."ID"=8)
5 - access("B"."FK_ID"(+)=8 AND "B"."COL1"(+)=8 AND "B"."COL2"(+) IS NULL
AND "B"."COL3"(+) IS NULL)
filter("B"."COL3"(+) IS NULL)