I need some help working out how to make my query more efficient. I’m by no means an expert with SQL but have read a fair bit regarding this but am at the point where I need to ask / discuss.
I have the following query:
SELECT * FROM
(
SELECT
a.seq_num seq_num_a,
a.record_length record_length_a,
b.record_length record_length_b,
c.record_length record_length_c
a.deleted deleted_a,
b.deleted deleted_b,
c.deleted deleted_c
FROM tableA a
INNER JOIN tableB b
ON a.reference = b.reference
AND a.run_id = b.run_id
INNER JOIN tableC c
ON a.reference = c.reference
AND a.run_id = c.run_id
WHERE a.run_id = 1
)
WHERE deleted_a = 'N'
AND deleted_b = 'N'
AND deleted_c = 'N'
ORDER BY seq_num_a;
The query is very slow once I check that deleted = ‘N’ – even for just tableA. I wrapped the whole query in a SELECT thinking that the subquery would return the dataset, then it would be a simple check and order against that. Note that there is no performance difference with the check within the subquery.
There are indexes against run_id and deleted in all tables, and against the seq_num in tableA. There are roughly 8000 rows in each table.
The query plan
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 112 | 7 |
| 1 | SORT ORDER BY | | 1 | 112 | 7 |
| 2 | NESTED LOOPS | | 1 | 112 | 3 |
| 3 | NESTED LOOPS | | 1 | 79 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| TABLEC | 1 | 33 | 2 |
|* 5 | INDEX SKIP SCAN | TABLEC_IDX2 | 1 | | 2 |
|* 6 | TABLE ACCESS BY INDEX ROWID| TABLEA | 1 | 46 | |
|* 7 | INDEX RANGE SCAN | TABLEA_IDX5 | 1 | | |
|* 8 | TABLE ACCESS BY INDEX ROWID | TABLEB | 1 | 33 | 1 |
|* 9 | INDEX UNIQUE SCAN | TABLEB_PK | 1 | | |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("C"."DELETED"='N')
filter("C"."DELETED"='N')
6 - filter("A"."REFERENCE"="C"."REFERENCE" AND
"A"."RUN_ID"="C"."RUN_ID")
7 - access("A"."DELETED"='N')
8 - filter("B"."DELETED"='N')
9 - access("A"."RUN_ID"="B"."RUN_ID" AND
"A"."REFERENCE"="B"."REFERENCE")
Note: cpu costing is off
Not sure what value the outer select was providing.. depending on the version of oracle the explain plans can be different. but in older versions I found limiting the data before the joins improved performance dramatically.
Thus starting with A and getting the least amount of records to join to b then to C was MUCH faster than starting with all of A, joining to all of b, and joining to all of C then reducing the results via where clause… However later verions of the query optimizer took this into account so no gain… So… what version of oracle?