Outer Join suppressing Index usage?
This is mostly a partial answer to part 1 with some speculation. You and I know that the following query:
select TD.LAB_NUMBER
from REQUEST_INFO RI
LEFT JOIN TEST_DATA TD ON TD.LAB_NUMBER = RI.LAB_NUMBER AND TD.SEQ_NUMBER = RI.SEQ_NUMBER
where UPPER(COALESCE(TD.SUPPL_FORMATTED_RESULT,TD.FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549;
Is equivalent to this query:
select TD.LAB_NUMBER
from REQUEST_INFO RI
INNER JOIN TEST_DATA TD ON
TD.LAB_NUMBER = RI.LAB_NUMBER
AND TD.SEQ_NUMBER = RI.SEQ_NUMBER
AND UPPER(COALESCE(TD.SUPPL_FORMATTED_RESULT,TD.FORMATTED_RESULT))='491(10)376'
and COALESCE(TD.SUPPL_IDNUM, TD.IDNUM)=40549;
However, that doesn't mean that Oracle knows that the two queries are equivalent. The equivalence of the two queries is required for Oracle to be able to use the TD_CUFR_CIDN_SN_LN
index. What we're hoping for here is an OUTER JOIN
to INNER JOIN
conversion. I haven't had a lot of luck finding good information about this, so let's look at the explain plans:
Adding TD.LAB_NUMBER IS NOT NULL
to the WHERE
clause is a very direct way to let Oracle know that OUTER JOIN
to INNER JOIN
conversion is possible. We can see that it occurred by looking at the highlighted line. I think that pretty much any column will allow the conversion, although picking the wrong column may change the query results.
If we try a slightly more complicated filter such as (TD.LAB_NUMBER IS NOT NULL OR TD.SEQ_NUMBER IS NOT NULL)
then the join conversion does not happen:
We can reason out that the OUTER JOIN
is really an INNER JOIN
but the query optimizer may have not been programmed to do that. In the original query, you have a COALESCE()
expression which is probably just too complex for the query optimizer to apply the query transformation.
Here is a db fiddle for some of the examples.
For the second question, I'm unable to think of a way to work around this. You could try taking advantage of table elimination. As you said this query doesn't even require the REQUEST_INFO
table. However, there are a few restrictions:
There are currently a few limitations of table elimination:
Multi-column primary key-foreign key constraints are not supported.
Referring to the join key elsewhere in the query will prevent table elimination. For an inner join, the join keys on each side of the join are equivalent, but if the query contains other references to the join key from the table that could otherwise be eliminated, this prevents elimination. A workaround is to rewrite the query to refer to the join key from the other table (we realize this is not always possible).
Perhaps there is a way to use that for this problem but I'm unable to work around the restrictions.