I am trying to improve the performance of the query shown below by rewriting a conditional LEFT JOIN
as a UNION
of INNER JOIN
and the base table. I am using Oracle 12c.
The tables in concern are ASSIGNMENTS
and CLASSES
. The requirement is that for a given ITEM_ID
value, we need to fetch details from the corresponding records in the two tables.
- There exists only one
CLASSES
table record each ITEM_ID. - The
ASSIGNMENTS
table may or may not have a record for any given ITEM_ID from the CLASSES table. - The
ASSIGNMENTS
record details are required only when the CLASSES record hasenable_capacity
= 'Y'.
The query that I have come up with is:
WITH CLASSES_WITH_CAPACITY AS (
SELECT maximum_attendees, item_id, enable_capacity
FROM CLASSES classes WHERE enable_capacity = 'Y' AND classes.item_id = 123
), CLASSES_WITHOUT_CAPACITY AS (
SELECT maximum_attendees, item_id, enable_capacity
FROM CLASSES classes WHERE enable_capacity is null AND item_id = 123
)
SELECT maximum_attendees, item_id, max_position_value,
enable_capacity, enable_waitlist FROM (
(SELECT classes.maximum_attendees, classes.item_id,
MAX(assignments.wait_position) max_position_value,
classes.enable_capacity, classes.enable_waitlist
FROM CLASSES_WITH_CAPACITY classes
JOIN WLF_ASSIGNMENT_RECORDS_F assignments ON (classes.item_id = assignments.item_id)
WHERE ( assignments.status <> 'EXPIRED')
GROUP BY classes.item_id, classes.maximum_attendees,
classes.enable_capacity, classes.enable_waitlist )
UNION ALL
(SELECT classes.maximum_attendees, classes.item_id,
null AS max_position_value,
classes.enable_capacity, classes.enable_waitlist
FROM CLASSES_WITHOUT_CAPACITY classes
)
);
In essence, the WITH clause statements will be mutually exclusive - that is, only one of the WITH clause queries will have the single CLASSES
record with the given ITEM_ID
. Below is the output of the EXPLAIN PLAN for the above query:
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 244 | 8 (13)| 00:00:01 |
| 1 | VIEW | | 2 | 244 | 8 (13)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
| 3 | HASH GROUP BY | | 1 | 111 | 6 (17)| 00:00:01 |
| 4 | MERGE JOIN CARTESIAN | | 1 | 111 | 5 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID | ASSIGNMENTS | 1 | 75 | 4 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | ASSIGNMENTS_N9 | 9 | | 1 (0)| 00:00:01 |
| 7 | BUFFER SORT | | 1 | 36 | 2 (50)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID BATCHED| CLASSES | 1 | 36 | 1 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | CLASSES_N2 | 1 | | 0 (0)| 00:00:01 |
|* 10 | TABLE ACCESS BY INDEX ROWID BATCHED | CLASSES | 1 | 36 | 2 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | CLASSES_N2 | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------
Now, my concern is regarding the join type being MERGE JOIN CARTESIAN
. It looks like it is using the MERGE JOIN CARTESIAN for the INNER JOIN
between CLASSES
and ASSIGNMENTS
.
My initial impression is that this won't lead to poor performance, only because the join would be performed on only one record. Is this correct?
What is the reason for the use of the Cartesian Join Type at all?
Given the fact that the Cartesian Join type is used when the JOIN predicates are missing and/or the joined predicates do not meet the proper primary-foreign key relationships, is it that the Optimizer does not find any indexes on the CLASSES alias? That is, is the use of the WITH clause obscuring away the indexes present in the table inside the WITH clause when joining the WITH Clause further?
If so, should the use of WITH clause be discouraged - especially when the output of the WITH clause is being joined further?
Also, any other suggestions on my approach are also welcome. Thanks!