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:

LAB_NUMBER

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:

no join conversion

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.