Is the order of conditions in a where clause relevant if both sides are left joined entities?
We identified the problem to be the Oracle setting OPTIMIZER_ADAPTIVE_PLANS and OPTIMIZER_ADAPTIVE_STATISTICS. By default after setting up the DB the first one is active and the second one is deactivated.
In our case both were activated. Right after deactivating OPTIMIZER_ADAPTIVE_STATISTICS (i.e. going back to the default settings) both queries returned the same results (as one would expect). These settings can be checked with the follwing query:
SELECT * FROM v$parameter WHERE 1 = 1 AND LOWER(name) LIKE LOWER('optimizer_ad%') ORDER BY 1 DESC;
Looking at your explain plans, it seems that the optimizer is rewriting your queries roughly like this:
First query:
select *
from tbl_bestand b
where ID_KB_ARTIKEL=3017874
and COD_LBR=12
and ID_ARTIKEL IS NOT NULL
and NVL(b.FLG_SPERRE,0)=0
union all
select b.*
from tbl_bestand b
left outer join tbl_kb_artikel c
on b.id_kb_artikel = c.id_kb_artikel
where COD_LBR=12
and ID_ARTIKEL=3017874
and NVL(b.FLG_SPERRE,0)=0
and LNNVL(c.ID_KB_ARTIKEL=3017874) -- ie, ID_KB_ARTIKEL <> 3017874 or ID_KB_ARTIKEL is null
;
Second query:
select *
from tbl_bestand b
where ID_ARTIKEL=3017874
and COD_LBR=12
and ID_KB_ARTIKEL IS NOT NULL -- this is the problem, you didn't want this
and NVL(b.FLG_SPERRE,0)=0
union all
select b.*
from tbl_bestand b
left outer join tbl_kb_artikel a
on b.id_kb_artikel = a.id_kb_artikel
where COD_LBR=12
and ID_KB_ARTIKEL=3017874
and NVL(b.FLG_SPERRE,0)=0
and LNNVL(c.ID_ARTIKEL=3017874) -- ie, ID_ARTIKEL <> 3017874 or ID_ARTIKEL is null
;
So it's getting rid of one of the outer joins in each case (since you aren't filtering on any other columns in the outer joined tables), but it switches depending on which condition you put first.
But @scaisEdge is right - if you see the IS NOT NULL
conditions, it's always turning one of the outer joins into an inner join. But I think you're also right, it seems like inconsistent optimizer behavior. I'm not sure why your other database isn't optimizing it the same way, but when I try your query with sample data, the explain plan just gets 2 hash join outer
s.
Anyway, when putting left join columns in the WHERE clause, I usually explicitly ask for NULLs to avoid this kind of thing. Does this version work as expected?
SELECT bestand0_.*
FROM tbl_bestand bestand0_
LEFT OUTER JOIN tbl_kb_artikel customeror1_
ON bestand0_.id_kb_artikel=customeror1_.id_kb_artikel
LEFT OUTER JOIN tbl_artikel article2_
ON bestand0_.id_artikel =article2_.id_artikel
WHERE ((article2_.id_artikel=3017874 and bestand0_.id_kb_artikel is null)
OR (customeror1_.id_kb_artikel=3017874 and bestand0_.id_artikel is null))
AND bestand0_.cod_lbr =12
AND NVL(bestand0_.flg_sperre,0) = 0;