I have an Oracle SQL query that is using a Correlated Subquery:
Q1
SELECT t1.id,
t3.code,
t3.processed_date,
(t1.total / t2.rate)
FROM table1 t1
JOIN table2 t2 ON t2.code= t1.code
JOIN table3 t3 ON t3.id = t1.id
JOIN table4 t4 ON t4.code = t1.code
AND t4.type IN ('value1', 'value2', 'value3')
AND t3.processed_date >= '01 JUL 2019'
AND t3.processed_date < '22 JUL 2019'
AND t2.effective_date IN (SELECT max(tc.effective_date)
FROM tableCore tc
WHERE tc.effective_date <= t3.processed_date
AND t1.code = tc.code)
I have changed the subquery to us an Empty OVER() caluse which has significantly improved the performance of this query:
Q2
SELECT t1.id,
t3.code,
t3.processed_date,
(t1.total / t2.rate)
FROM table1 t1
JOIN table2 t2 ON t2.code= t1.code
JOIN table3 t3 ON t3.id = t1.id
JOIN table4 t4 ON t4.code = t1.code
AND t4.type IN ('value1', 'value2', 'value3')
AND t3.processed_date >= '01 JUL 2019'
AND t3.processed_date < '22 JUL 2019'
AND t2.effective_date IN (SELECT max(tc.effective_date) OVER () AS ed
FROM tableCore tc
WHERE tc.effective_date <= t3.processed_date
AND t1.code = tc.code)
The new query returns the same result set as the original query, so appears to be working..., but why is the explain plan so different, it appears to be still correlated, is it not evaluating for every row in the outer query anymore? why?
I want to understand what is going on in the second query.
I think I could re-write this query a 3rd way using row_number() OVER (partition by ...)
:
Q3
SELECT t1.id,
t3.code,
t3.processed_date,
(t1.total / t2.rate),
ct.*
FROM table1 t1
JOIN table2 t2 ON t2.code = t1.code
JOIN table3 t3 ON t3.id = t1.id
JOIN table4 t4 ON t4.code = t1.code
JOIN (SELECT ct.*, row_number() OVER (PARTITION BY ct.code ORDER BY ct.effective_date ASC) AS rn
FROM tablecore ct) ct
ON t1.code = ct.code
AND rn = 1
AND ct.effective_date <= t3.processed_date
WHERE t2.effective_date in(ct.effective_date)
AND t4.type IN ('value1', 'value2', 'value3')
AND t3.processed_date >= '01 JUL 2019'
AND t3.processed_date < '22 JUL 2019'
AND t2.effective_date IN (ct.effective_date);
This version seems to work too, but is slower than the second version.
EDIT AS pointed out by @Christian Q3 will return incorrect results