Reputation: 287
I am running this query in production(Oracle) and it is taking more than 3 minutes . Is there any way out to reduce the execution time ? Both svc_order and event table contains almost 1million records .
select 0 test_section, count(1) count, 'DD' test_section_value
from svc_order so, event e
where so.svc_order_id = e.svc_order_id
and so.entered_date >= to_date('01/01/2012', 'MM/DD/YYYY')
and e.event_type = 230 and e.event_level = 'O'
and e.current_sched_date between
to_date( '09/01/2010 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
and to_date('09/29/2013 23:59:59', 'MM/DD/YYYY HH24:MI:SS')
and (((so.sots_ta = 'N') and (so.action_type = 0))
or ((so.sots_ta is null) and (so.action_type = 0))
or ((so.sots_ta = 'N') and (so.action_type is null)))
and so.company_code = 'LL'
Upvotes: 1
Views: 20249
Reputation: 5225
Looking at the what you said that you cannot create indexes. I hope that the query is making a full table scan on the table. Please try a parallel hint.
select /*+ full(so) parallel(so, 4) */ 0 test_section, count(1) count, 'DD' test_section_value
from svc_order so, event e
where so.svc_order_id = e.svc_order_id
and so.entered_date >= to_date('01/01/2012', 'MM/DD/YYYY')
and e.event_type = 230 and e.event_level = 'O'
and e.current_sched_date between
to_date( '09/01/2010 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
and to_date('09/29/2013 23:59:59', 'MM/DD/YYYY HH24:MI:SS')
and (((so.sots_ta = 'N') and (so.action_type = 0))
or ((so.sots_ta is null) and (so.action_type = 0))
or ((so.sots_ta = 'N') and (so.action_type is null)))
and so.company_code = 'LL'
Upvotes: 1
Reputation: 36817
First, ensure statistics are up-to-date.
begin
dbms_stats.gather_table_stats('[schema]', 'svc_order');
dbms_stats.gather_table_stats('[schema]', 'event');
end;
/
This query is a very simple join between two small tables, but with complex predicates.
You almost certainly do not want to significantly re-write all of your queries in search of some magic syntax that will make everything run fast. Yes, there are some rare cases where BETWEEN
does not work well, or moving the predicates into an inline view helps, or replacing the join with an INTERSECT
might help. But that sounds like cargo-cult programming to me. Ask yourself, why would those changes make any difference? If those types of changes always improved performance, why wouldn't Oracle just translate the queries internally?
Normally, you should try to provide better information to the optimizer so it can make better decisions. Usually this is as simple as gathering statistics with the default settings. Some predicates are just too complex, and for that you should try to use
dynamic sampling, such as /*+ dynamic_sampling(6) */
. Or maybe
add some histograms. Or perhaps add an expression statistic like this:
SELECT
DBMS_STATS.CREATE_EXTENDED_STATS(null,'SVC_ORDER',
'(((so.sots_ta = 'N') and (so.action_type = 0))
or ((so.sots_ta is null) and (so.action_type = 0))
or ((so.sots_ta = 'N') and (so.action_type is null)))'
)
FROM DUAL;
--Don't forget to re-gather statistics after this.
The optimizer is probably under-estimating the number of rows, and using a nested loop instead of a hash join. After providing it with more information, ideally it will start using a hash join. But at some point, after you've tried the above methods and possibly many other features, you can just tell it what kind of join to use. Which would be @Florin Ghita's suggestion, /*+use_hash(so e)*/
.
Upvotes: 0
Reputation: 1254
We cannot have additional indexes but tables must have at least meaning full primary key, right so is there one? That should result in at least index, non/clustered, anything. Look at it lets and try to make use of it.
In case table is a heap, and we want to deal with it as it is, then we should reduce the number rows in each table individually by applying respective where filters and then combine that result set. In your query only meaning full result column depends on base tables is count(1). Other two columns are constants. Because also JOIN/Cartesian Product etc….. will lead DB engine to look for Indexes so instead use INTERSECT which I feel should better in your case. Some other changes you can do: Avoid using TO_DATE or any kind of function in Right Side of the WHERE condition column. Prepare data in local Variable and use Local Variable in query. Also you need to check is there any good performance gain using >= than BETWEEN ?
I have modified the query and also combined one redundant where condition. Remember that if this changes works for you right now that doesn’t mean it will work always. As son your table start hitting more data that qualifies those WHERE conditions this swill again come back as slow query. so for short term this might work but longer term you have to think about alternate options
1) for example Indexed Views on top of this tables
2) Create same tables with different name and sync data
between new and original table using “Insert/Update/Delete Trigger”.
SELECT COUNT(1) AS [COUNT], 'DD' test_section_value ,0 test_section
FROM
(
SELECT so.svc_order_id
FROM svc_order so
WHERE so.entered_date >= to_date('01/01/2012', 'MM/DD/YYYY')
AND so.company_code = 'LL'
INTERSECT
SELECT e.svc_order_id
FROM event e
WHERE e.event_type = 230
AND e.event_level = 'O'
AND e.current_sched_date BETWEEN
to_date('09/01/2010 00:00:00','MM/DD/YYYY HH24:MI:SS')
AND to_date('09/29/2013 23:59:59','MM/DD/YYYY HH24:MI:SS')
AND (
(( so.sots_ta = 'N' ) AND ( so.action_type IS NULL OR so.action_type = 0))
OR
(( so.sots_ta IS NULL ) AND ( so.action_type = 0 ))
--or ((so.sots_ta = 'N') and (so.action_type is null))
)
)qry1
Upvotes: 0
Reputation: 44250
You could at least avoid the triple AND/OR list by using COALESCE()
(or its oracle equivalent IFNULL()
) Note: this does not catch the case where both sots_ta and action_type are NULL.
SELECT 0 test_section, count(1) count, 'DD' test_section_value
FROM svc_order so
JOIN event e ON so.svc_order_id = e.svc_order_id
WHERE e.event_type = 230 and e.event_level = 'O'
AND so.entered_date >= to_date('01/01/2012', 'MM/DD/YYYY')
AND e.current_sched_date >= to_date('09/01/2010 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
AND e.current_sched_date < to_date('10/01/2013 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
AND COALESCE(so.sots_ta, 'N') = 'N'
AND COALESCE(so.action_type, 0) = 0
AND so.company_code = 'LL'
I replaced the between by a plain t >= low AND t. < high)
test because I don't like between
s semantics. I replaced the FROM kommalist
by a JOIN
because I like joins better.
Upvotes: 0