The Situation
I have some trouble with my query execution plan for a medium-sized query over a large amount of data in Oracle 11.2.0.2.0. In order to speed things up, I introduced a range filter that does roughly something like this:
PROCEDURE DO_STUFF(
org_from VARCHAR2 := NULL,
org_to VARCHAR2 := NULL)
-- [...]
JOIN organisations org
ON (cust.org_id = org.id
AND ((org_from IS NULL) OR (org_from <= org.no))
AND ((org_to IS NULL) OR (org_to >= org.no)))
-- [...]
As you can see, I want to restrict the JOIN
of organisations
using an optional range of organisation numbers. Client code can call DO_STUFF
with (supposed to be fast) or without (very slow) the restriction.
The Trouble
The trouble is, PL/SQL will create bind variables for the above org_from
and org_to
parameters, which is what I would expect in most cases:
-- [...]
JOIN organisations org
ON (cust.org_id = org.id
AND ((:B1 IS NULL) OR (:B1 <= org.no))
AND ((:B2 IS NULL) OR (:B2 >= org.no)))
-- [...]
The Workaround
Only in this case, I measured the query execution plan to be a lot better when I just inline the values, i.e. when the query executed by Oracle is actually something like
-- [...]
JOIN organisations org
ON (cust.org_id = org.id
AND ((10 IS NULL) OR (10 <= org.no))
AND ((20 IS NULL) OR (20 >= org.no)))
-- [...]
By "a lot", I mean 5-10x faster. Note that the query is executed very rarely, i.e. once a month. So I don't need to cache the execution plan.
My questions
How can I inline values in PL/SQL? I know about EXECUTE IMMEDIATE, but I would prefer to have PL/SQL compile my query, and not do string concatenation.
Did I just measure something that happened by coincidence or can I assume that inlining variables is indeed better (in this case)? The reason why I ask is because I think that bind variables force Oracle to devise a general execution plan, whereas inlined values would allow for analysing very specific column and index statistics. So I can imagine that this is not just a coincidence.
Am I missing something? Maybe there is an entirely other way to achieve query execution plan improvement, other than variable inlining (note I have tried quite a few hints as well but I'm not an expert on that field)?
NO
column inORGANIZATIONS
is a unique key? Are the statistics on theNO
column accurate? – FairgroundNO
is unique, statistics are accurate. – Fortyfiveorganisations
table. That one only has around 550 records. Every organisation has 100k+ records in various other tables that are involved with the full query though – FortyfiveEXECUTE IMMEDIATE
. I rearranged the order of questions... – FortyfiveBETWEEN
for the range filter? Might that help? – WackBETWEEN
keyword results in almost the same execution plan. Probably, some very unorthodox index could resolve the problem beyond my understanding. But it wouldn't explain why inlined values perform so much better than bind variables. I think I'm gonna have to post the query... :-) – Fortyfiveorg.no
is aVARCHAR2
column, but it only contains 5-digit numbers. So I guess, in my inline example, Oracle does automatic casting orto_char
or something like that. I'll check if that has any meaning for execution plans (I would be surprised though) – Fortyfive