Explain Plan of Oracle With Clause shows Merge Join Cartesian
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!
oracle oracle12c query-performance sql-execution-plan
|
show 2 more comments
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!
oracle oracle12c query-performance sql-execution-plan
Yes, the Oracle impression is that theMERGE JOIN CARTESIAN
will be performed with one row. See the correspondingRows
column. Also all tables are accesed withINDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.
– Marmite Bomber
Nov 16 '18 at 6:29
1
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
1
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43
|
show 2 more comments
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!
oracle oracle12c query-performance sql-execution-plan
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!
oracle oracle12c query-performance sql-execution-plan
oracle oracle12c query-performance sql-execution-plan
edited Nov 16 '18 at 6:33
Sarath Chandra
asked Nov 16 '18 at 6:01
Sarath ChandraSarath Chandra
1,6001532
1,6001532
Yes, the Oracle impression is that theMERGE JOIN CARTESIAN
will be performed with one row. See the correspondingRows
column. Also all tables are accesed withINDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.
– Marmite Bomber
Nov 16 '18 at 6:29
1
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
1
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43
|
show 2 more comments
Yes, the Oracle impression is that theMERGE JOIN CARTESIAN
will be performed with one row. See the correspondingRows
column. Also all tables are accesed withINDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.
– Marmite Bomber
Nov 16 '18 at 6:29
1
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
1
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43
Yes, the Oracle impression is that the
MERGE JOIN CARTESIAN
will be performed with one row. See the corresponding Rows
column. Also all tables are accesed with INDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.– Marmite Bomber
Nov 16 '18 at 6:29
Yes, the Oracle impression is that the
MERGE JOIN CARTESIAN
will be performed with one row. See the corresponding Rows
column. Also all tables are accesed with INDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.– Marmite Bomber
Nov 16 '18 at 6:29
1
1
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
1
1
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43
|
show 2 more comments
1 Answer
1
active
oldest
votes
Why is used CARTESIAN JOIN?
As you stated there is at most one record in the table CLASSES
with a given item_id
.
Oracle realizes this (see colum Rows
in the line 9 of the execution plan) and decides to make a cartesian join of this one row
with the expected 9 rows of the table ASSIGNMENTS
.
This works fine if the cardinality estimation is fine, but can cause big troubles if it is inaccurate. I suppose that the item_id
is not backed by a primary key or a unique index - see the INDEX RANGE SCAN
in line 9; I'd expect INDEX UNIQUE SCAN
in case of unique index. So this is a potential cause of problems.
Usage of WITH
clauses
As mentioned by others the WITH
clauses in your query has no effect, but also makes no harm. As you see in the execution plan they are
merged in the main query. Oracle is re-writing the query and eliminates both subqueries.
Improve the Performance
You din't stated what problems you tried to approach by eliminating LEFT OUTER JOIN
; anyway there could be a subtle point where some care should
be taken.
Basically there are two possibilities to execute the plan
1) join first and than aggregate to get the MAX(wait_position)
2) get the MAX value first and than join (trivial) the two one row tables
If the table ASSINMENT
has only imited number of rows with the given ITEM_ID
there is practically no difference between those two options.
The troubles starts if you encounters an assignment with tons of rows for some ITEM_ID
. This is sometimes called as a dying in the NESTED LOOPS as
you loop on millions of rows only after that aggregate the result to one row containing the MAX
.
Oracle has for this case an index access INDEX RANGE SCAN (MIN/MAX)
which get the maximum value direct from the index (without accessing the table and without scanning all the values - remember index is sorted, so get MAX value is trivial).
So under the assumtion of those indexs, you may try the alternative query below
unique index on classes(item_id)
index on WLF_ASSIGNMENT_RECORDS_F(item_id, wait_position)
The second index is important as you will bypass the table at all and you it to the the max(wait_position)
The query (simplified by ommiting some columns) is as follows:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Note that the subquery calculates the max(wait_position)
.
In the next step you outer joins
the table to the subquery, but only if c.enable_capacity = 'Y' - no further predicate is needed as both row sources has maximal one row.
Execution plan is very effective consisting of tow index access and one table block access.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
I like this example as a demonstration, that a simple and compact written query leads to an effective execution plan.
Final remark, I hope in your production environment you use bind variables and not literal keys for the item_id
;)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53332275%2fexplain-plan-of-oracle-with-clause-shows-merge-join-cartesian%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Why is used CARTESIAN JOIN?
As you stated there is at most one record in the table CLASSES
with a given item_id
.
Oracle realizes this (see colum Rows
in the line 9 of the execution plan) and decides to make a cartesian join of this one row
with the expected 9 rows of the table ASSIGNMENTS
.
This works fine if the cardinality estimation is fine, but can cause big troubles if it is inaccurate. I suppose that the item_id
is not backed by a primary key or a unique index - see the INDEX RANGE SCAN
in line 9; I'd expect INDEX UNIQUE SCAN
in case of unique index. So this is a potential cause of problems.
Usage of WITH
clauses
As mentioned by others the WITH
clauses in your query has no effect, but also makes no harm. As you see in the execution plan they are
merged in the main query. Oracle is re-writing the query and eliminates both subqueries.
Improve the Performance
You din't stated what problems you tried to approach by eliminating LEFT OUTER JOIN
; anyway there could be a subtle point where some care should
be taken.
Basically there are two possibilities to execute the plan
1) join first and than aggregate to get the MAX(wait_position)
2) get the MAX value first and than join (trivial) the two one row tables
If the table ASSINMENT
has only imited number of rows with the given ITEM_ID
there is practically no difference between those two options.
The troubles starts if you encounters an assignment with tons of rows for some ITEM_ID
. This is sometimes called as a dying in the NESTED LOOPS as
you loop on millions of rows only after that aggregate the result to one row containing the MAX
.
Oracle has for this case an index access INDEX RANGE SCAN (MIN/MAX)
which get the maximum value direct from the index (without accessing the table and without scanning all the values - remember index is sorted, so get MAX value is trivial).
So under the assumtion of those indexs, you may try the alternative query below
unique index on classes(item_id)
index on WLF_ASSIGNMENT_RECORDS_F(item_id, wait_position)
The second index is important as you will bypass the table at all and you it to the the max(wait_position)
The query (simplified by ommiting some columns) is as follows:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Note that the subquery calculates the max(wait_position)
.
In the next step you outer joins
the table to the subquery, but only if c.enable_capacity = 'Y' - no further predicate is needed as both row sources has maximal one row.
Execution plan is very effective consisting of tow index access and one table block access.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
I like this example as a demonstration, that a simple and compact written query leads to an effective execution plan.
Final remark, I hope in your production environment you use bind variables and not literal keys for the item_id
;)
add a comment |
Why is used CARTESIAN JOIN?
As you stated there is at most one record in the table CLASSES
with a given item_id
.
Oracle realizes this (see colum Rows
in the line 9 of the execution plan) and decides to make a cartesian join of this one row
with the expected 9 rows of the table ASSIGNMENTS
.
This works fine if the cardinality estimation is fine, but can cause big troubles if it is inaccurate. I suppose that the item_id
is not backed by a primary key or a unique index - see the INDEX RANGE SCAN
in line 9; I'd expect INDEX UNIQUE SCAN
in case of unique index. So this is a potential cause of problems.
Usage of WITH
clauses
As mentioned by others the WITH
clauses in your query has no effect, but also makes no harm. As you see in the execution plan they are
merged in the main query. Oracle is re-writing the query and eliminates both subqueries.
Improve the Performance
You din't stated what problems you tried to approach by eliminating LEFT OUTER JOIN
; anyway there could be a subtle point where some care should
be taken.
Basically there are two possibilities to execute the plan
1) join first and than aggregate to get the MAX(wait_position)
2) get the MAX value first and than join (trivial) the two one row tables
If the table ASSINMENT
has only imited number of rows with the given ITEM_ID
there is practically no difference between those two options.
The troubles starts if you encounters an assignment with tons of rows for some ITEM_ID
. This is sometimes called as a dying in the NESTED LOOPS as
you loop on millions of rows only after that aggregate the result to one row containing the MAX
.
Oracle has for this case an index access INDEX RANGE SCAN (MIN/MAX)
which get the maximum value direct from the index (without accessing the table and without scanning all the values - remember index is sorted, so get MAX value is trivial).
So under the assumtion of those indexs, you may try the alternative query below
unique index on classes(item_id)
index on WLF_ASSIGNMENT_RECORDS_F(item_id, wait_position)
The second index is important as you will bypass the table at all and you it to the the max(wait_position)
The query (simplified by ommiting some columns) is as follows:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Note that the subquery calculates the max(wait_position)
.
In the next step you outer joins
the table to the subquery, but only if c.enable_capacity = 'Y' - no further predicate is needed as both row sources has maximal one row.
Execution plan is very effective consisting of tow index access and one table block access.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
I like this example as a demonstration, that a simple and compact written query leads to an effective execution plan.
Final remark, I hope in your production environment you use bind variables and not literal keys for the item_id
;)
add a comment |
Why is used CARTESIAN JOIN?
As you stated there is at most one record in the table CLASSES
with a given item_id
.
Oracle realizes this (see colum Rows
in the line 9 of the execution plan) and decides to make a cartesian join of this one row
with the expected 9 rows of the table ASSIGNMENTS
.
This works fine if the cardinality estimation is fine, but can cause big troubles if it is inaccurate. I suppose that the item_id
is not backed by a primary key or a unique index - see the INDEX RANGE SCAN
in line 9; I'd expect INDEX UNIQUE SCAN
in case of unique index. So this is a potential cause of problems.
Usage of WITH
clauses
As mentioned by others the WITH
clauses in your query has no effect, but also makes no harm. As you see in the execution plan they are
merged in the main query. Oracle is re-writing the query and eliminates both subqueries.
Improve the Performance
You din't stated what problems you tried to approach by eliminating LEFT OUTER JOIN
; anyway there could be a subtle point where some care should
be taken.
Basically there are two possibilities to execute the plan
1) join first and than aggregate to get the MAX(wait_position)
2) get the MAX value first and than join (trivial) the two one row tables
If the table ASSINMENT
has only imited number of rows with the given ITEM_ID
there is practically no difference between those two options.
The troubles starts if you encounters an assignment with tons of rows for some ITEM_ID
. This is sometimes called as a dying in the NESTED LOOPS as
you loop on millions of rows only after that aggregate the result to one row containing the MAX
.
Oracle has for this case an index access INDEX RANGE SCAN (MIN/MAX)
which get the maximum value direct from the index (without accessing the table and without scanning all the values - remember index is sorted, so get MAX value is trivial).
So under the assumtion of those indexs, you may try the alternative query below
unique index on classes(item_id)
index on WLF_ASSIGNMENT_RECORDS_F(item_id, wait_position)
The second index is important as you will bypass the table at all and you it to the the max(wait_position)
The query (simplified by ommiting some columns) is as follows:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Note that the subquery calculates the max(wait_position)
.
In the next step you outer joins
the table to the subquery, but only if c.enable_capacity = 'Y' - no further predicate is needed as both row sources has maximal one row.
Execution plan is very effective consisting of tow index access and one table block access.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
I like this example as a demonstration, that a simple and compact written query leads to an effective execution plan.
Final remark, I hope in your production environment you use bind variables and not literal keys for the item_id
;)
Why is used CARTESIAN JOIN?
As you stated there is at most one record in the table CLASSES
with a given item_id
.
Oracle realizes this (see colum Rows
in the line 9 of the execution plan) and decides to make a cartesian join of this one row
with the expected 9 rows of the table ASSIGNMENTS
.
This works fine if the cardinality estimation is fine, but can cause big troubles if it is inaccurate. I suppose that the item_id
is not backed by a primary key or a unique index - see the INDEX RANGE SCAN
in line 9; I'd expect INDEX UNIQUE SCAN
in case of unique index. So this is a potential cause of problems.
Usage of WITH
clauses
As mentioned by others the WITH
clauses in your query has no effect, but also makes no harm. As you see in the execution plan they are
merged in the main query. Oracle is re-writing the query and eliminates both subqueries.
Improve the Performance
You din't stated what problems you tried to approach by eliminating LEFT OUTER JOIN
; anyway there could be a subtle point where some care should
be taken.
Basically there are two possibilities to execute the plan
1) join first and than aggregate to get the MAX(wait_position)
2) get the MAX value first and than join (trivial) the two one row tables
If the table ASSINMENT
has only imited number of rows with the given ITEM_ID
there is practically no difference between those two options.
The troubles starts if you encounters an assignment with tons of rows for some ITEM_ID
. This is sometimes called as a dying in the NESTED LOOPS as
you loop on millions of rows only after that aggregate the result to one row containing the MAX
.
Oracle has for this case an index access INDEX RANGE SCAN (MIN/MAX)
which get the maximum value direct from the index (without accessing the table and without scanning all the values - remember index is sorted, so get MAX value is trivial).
So under the assumtion of those indexs, you may try the alternative query below
unique index on classes(item_id)
index on WLF_ASSIGNMENT_RECORDS_F(item_id, wait_position)
The second index is important as you will bypass the table at all and you it to the the max(wait_position)
The query (simplified by ommiting some columns) is as follows:
with max_wait as (
select max(a.wait_position) max_wait_position
from assignments a
where a.item_id = 1)
select c.item_id, m.max_wait_position
from classes c
left outer join max_wait m
on c.enable_capacity = 'Y'
where c.item_id = 1;
Note that the subquery calculates the max(wait_position)
.
In the next step you outer joins
the table to the subquery, but only if c.enable_capacity = 'Y' - no further predicate is needed as both row sources has maximal one row.
Execution plan is very effective consisting of tow index access and one table block access.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 1 | 18 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | CLASSES | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | CLASSES_UX1 | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | VW_LAT_1D42B1AA | 1 | 13 | 2 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 9 | | |
| 8 | FIRST ROW | | 1 | 9 | 2 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN (MIN/MAX)| ASSIGNMENTS_IX1 | 1 | 9 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C"."ITEM_ID"=1)
5 - filter("C"."ENABLE_CAPACITY"='Y')
9 - access("A"."ITEM_ID"=1)
I like this example as a demonstration, that a simple and compact written query leads to an effective execution plan.
Final remark, I hope in your production environment you use bind variables and not literal keys for the item_id
;)
edited Nov 24 '18 at 17:49
answered Nov 17 '18 at 20:46
Marmite BomberMarmite Bomber
8,21231135
8,21231135
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53332275%2fexplain-plan-of-oracle-with-clause-shows-merge-join-cartesian%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Yes, the Oracle impression is that the
MERGE JOIN CARTESIAN
will be performed with one row. See the correspondingRows
column. Also all tables are accesed withINDEX RANGE SCAN
(lines 6, 9, 11). If you look at the Predicate section of the execution plan (that is cut off) you will see the access and filter predicates. See here how to get full execution plan.– Marmite Bomber
Nov 16 '18 at 6:29
1
You get no value from the WITH clauses here. You could simplify the query immensely by just having both sides of the UNION ALL operator query directly from CLASSES.
– APC
Nov 16 '18 at 6:58
@APC Do you mean SELECT ... FROM ASSIGNMENTS JOIN CLASSES where enable_capacity = 'Y' UNION ALL SELECT ... FROM CLASSES where enable_capacity IS NULL?
– Sarath Chandra
Nov 16 '18 at 8:33
Yes that's what I mean
– APC
Nov 16 '18 at 9:04
1
The amount of data you're working with is too small to draw any conclusions. But generally, the answer is it depends. WITH clauses can be very performative when used properly e.g. when a subquery is used more than once or to handle recursion. But they can also have negative impact, say if the optimizer decides to materialize them and that turns out to be a bad option. So the better practice is: understand what WITH clause does and only use it when appropriate to do so.
– APC
Nov 16 '18 at 9:43