I have a data re-population scheme for a set of oracle 11g database tables that entails:
- Drop and recreate all tables. Drop is like
DROP TABLE TCA_SECURITY_DAY_BAR CASCADE CONSTRAINTS PURGE - Load static data - i.e. lookup tables, then load some data associated with dates
At various times in load process I intersperse gather statistics commands, which I've found greatly sped up various queries of load process itself. However, I have little confidence in my options because they were selected via guesswork.
- Targeted table gather invocations. Example:
dbms_stats.gather_table_stats( ownname => 'TCA', tabname => 'TCA_SECURITY_DAY_BAR', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, block_sample => TRUE, method_opt => 'FOR ALL INDEXED COLUMNS SIZE 1', cascade => TRUE );- Schema invocations: Example:
dbms_stats.gather_schema_stats( ownname => 'TCA', estimate_percent => NULL, block_sample => TRUE, method_opt => 'FOR ALL INDEXED COLUMNS SIZE 1', cascade => TRUE, granularity => 'ALL', options => 'GATHER' )
After a reload process I run some reporting and I have class of problem query that is taking 7 seconds when it can/should be sub-second. I already have improvements for the queries, such as using parameters and being more specific in the where clause. With this question I'm not trying to improve the query but rather trying to improve the plan selected by the engine or at least understand its mysterious behavior.
The queries are generated from a list of column expressions (non-grouped, aggregated) where I chunk the list into some number of columns (e.g. 3, 4, 5) to see what visually looks best in the report. For a given set of join clauses the number of columns expressions selected and some other factor (time of day, caching, temperature, who knows) is effecting whether the optimizer is choosing a fast plan or a slow plan.
Here is the sample query with 1 grouped column multiplier and 5 non-grouped aggregated expressions:
SELECT
TO_.multiplier
-- multiplier.bracket
, min(TCA.TCA_SECURITY.symbol)
-- symbol.min
, min(TO_.px_limit)
-- px_limit.min
, min(TCA.TCA_SECURITY_DAY_BAR.px_open_day)
-- px_open_day.min
, min(TCA.TCA_SECURITY_DAY_BAR.px_high_day)
-- px_high_day.min
, min(TCA.TCA_SECURITY_DAY_BAR.px_low_day)
-- px_low_day.min
FROM TCA_ORDER TO_
LEFT JOIN TCA_ORDER_SECURITY ON (TO_.ID = TCA_ORDER_SECURITY.ORDER_ID)
LEFT JOIN TCA.TCA_SECURITY TCA_SECURITY ON (TCA_ORDER_SECURITY.SECURITY_ID = TCA_SECURITY.ID)
LEFT JOIN TCA.TCA_SECURITY_DAY_BAR ON (
TCA_SECURITY.ID = TCA_SECURITY_DAY_BAR.SECURITY_ID
AND TO_.TRADE_DATE = TCA_SECURITY_DAY_BAR.TRADE_DATE
)
WHERE TRADING_UNIT_ID IN (621)
GROUP BY TO_.multiplier
Observations:
- When comparing the queries with different numbers of columns I'm not running statistics in between and I believe they are not being run (since setup is to do it nightly).
- Once a pattern of a slow query is in place it is consistent for some time.
- Initially, I found that the query with 5 columns was always slow (> 7 seconds)
- If I only had 4 it was fast.
- The next day 5 was suddenly fast. But selecting 4 columns was slow.
- When I copy/pasted the logged slow running queries into SQL Developer they were fast
- As of this morning - 3 is fast, 4 is slow and 5 is fast
I know the schema is setup to have statistics run nightly, but I don't really know the exact command used for that. If those nightly statistics gathering calls are not special and I could call them myself on-demand that may help - I'd just need to know how to exactly mimic it.
To try to troubleshoot, in my kotlin/jdbc code I've instrumented with calls to explain last plan run. Here is a diff showing a query with 5 columns that is fast and 4 columns that is slow. It is clear that for some reason it is selecting "NESTED LOOPS OUTER" in the slow case and I don't know why.
So I'm looking for an explanation of what may be going on and suggestions to impact the optimizer's choice without changing the query.
