如何减少具有大量数据的表的查询执行时间

时间:2013-10-02 12:03:15

标签: sql database performance oracle

我在生产(Oracle)中运行此查询,并且需要超过3分钟。有没有办法减少执行时间? svc_order和事件表都包含近100万条记录。

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'

4 个答案:

答案 0 :(得分:1)

看看你所说的你无法创建索引。我希望查询在表上进行全表扫描。请尝试平行提示。

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'

答案 1 :(得分:0)

你可以至少使用COALESCE()(或者它的oracle等价物IFNULL())来避免三重AND / OR列表注意:这不会出现 sots_ta和action_type都是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'

我用普通的t >= low AND t. < high)测试替换了它,因为我不喜欢between的语义。我将FROM kommalist替换为JOIN因为我喜欢加入更好。

答案 2 :(得分:0)

我们不能有其他索引但是表格必须至少具有完整的主键含义,那么是否有一个?这应该至少导致索引,非/群集,任何东西。 看看它让我们尝试使用它。

如果表是一个堆,并且我们想要按原样处理它,那么我们应该通过应用相应的where过滤器然后组合该结果集来单独减少每个表中的行数。 在您的查询中,仅表示完整结果列依赖于基表是count(1)。其他两列是常量。 因为还有JOIN / Cartesian产品等......会引导数据库引擎寻找索引,所以改用INTERSECT,我觉得在你的情况下应该更好。 您可以执行的其他一些更改: 避免在WHERE条件列的右侧使用TO_DATE或任何类型的函数。在本地变量中准备数据并在查询中使用本地变量。 你还需要检查使用&gt; =比BETWEEN有任何好的性能提升吗?

我已经修改了查询并且还合并了一个冗余where条件。 请记住,如果此更改现在适用于您,并不意味着它始终有效。作为儿子你的桌子开始点击更多数据,这些数据符合那些WHERE条件,这个swill再次回来作为慢查询。因此,从短期来看,这可能有效,但从长期来看,你必须考虑其他选择

    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

答案 3 :(得分:0)

首先,确保统计数据是最新的。

begin
    dbms_stats.gather_table_stats('[schema]', 'svc_order');
    dbms_stats.gather_table_stats('[schema]', 'event');
end;
/

此查询是两个小表之间非常简单的连接,但具有复杂谓词。 你几乎可以肯定想要重新编写所有查询,以寻找一些能让一切运行得很快的神奇语法。是的,在某些罕见的情况下,BETWEEN无效,或者将谓词移动到内联视图帮助,或者用INTERSECT替换连接可能会有所帮助。但这对我来说听起来像cargo-cult programming。问问自己,为什么这些变化会有什么不同?如果这些类型的更改总能提高性能,为什么Oracle不会在内部翻译查询?

通常,您应该尝试向优化器提供更好的信息,以便做出更好的决策。通常,这与使用默认设置收集统计信息一样简单。有些谓词太复杂了,为此你应该尝试使用 dynamic sampling,例如/*+ dynamic_sampling(6) */。或者可能 添加一些histograms。或者像这样添加expression statistic

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.

优化器可能低估了行数,并使用嵌套循环而不是散列连接。在为其提供更多信息之后,理想情况下它将开始使用散列连接。但是在某些时候,在您尝试了上述方法以及可能的许多其他功能之后,您可以告诉它使用哪种连接。这将是@Florin Ghita的建议,/*+use_hash(so e)*/