order by子句的性能问题

时间:2012-02-25 05:46:07

标签: sql performance oracle oracle11g

我有下面的查询,它在执行后从100000000返回20000条记录..

SELECT * 
  FROM (SELECT a.*, ROWNUM rnum 
          FROM (select TIME,url,bytes 
                  FROM (SELECT TO_CHAR ("5mintime",'YYYY-MM-DD HH24:MI:SS') AS TIME, url, BYTES 
                          FROM available_web_details  
                         WHERE "5mintime" >= TIMESTAMP '2012-02-10 00:00:00' 
                           AND "5mintime" <= TIMESTAMP '2012-02-13 23:59:59' 
                           AND username = 'asha1328874833' 
                           AND CATEGORY = 'None240001'AND domain = '1328874833.vmware.com.'
                           AND (appid IN ('216.198.204.192id','216.198.207.0id','216.198.211.64id','216.198.211.128id','216.198.212.0id', '216.198.214.128id','216.198.214.192id','216.198.218.0id','216.198.220.192id','216.198.222.0id','216.198.223.32id','216.198.228.128id','216.198.229.128id',.....))) 
                      ORDER BY TIME DESC) a 
                 WHERE ROWNUM <= 10) 
         WHERE rnum > 0

执行它大约需要5分钟。但是当我删除order by子句时,它会在4秒内执行。你能建议我如何改善表现?

available_web_details的架构:

Name         Null   Type           
------------ ---- -------------- 
5mintime          TIMESTAMP(6)   
USERNAME          VARCHAR2(64)   
HOST              NUMBER         
SRC_ZONE          VARCHAR2(32)   
DOMAIN            VARCHAR2(512)  
DST_ZONE          VARCHAR2(32)   
CONTENT           VARCHAR2(64)   
CATEGORY          VARCHAR2(64)   
URL               VARCHAR2(1024) 
HITS              NUMBER         
BYTES             NUMBER         
APPID             VARCHAR2(32)   
APPLICATION       VARCHAR2(64)   
CATEGORYTYPE      VARCHAR2(64)   
USERGROUP         VARCHAR2(384)  

我在 appid,“5mintime”上有本地分区的索引。 available_web_details是1个月的范围分区表。

3 个答案:

答案 0 :(得分:2)

让我们从免责声明开始:如果您不删除order by或开始使用临时表,则执行时间永远不会减少到4秒。第二个免责声明:我犯了一个大错误,我现在正在纠正;我花了很长时间才意识到这一点。谢谢Alex Poole

我的观点(我总是有几个)。

  1. 评论中提出了一些非常有效的观点,特别是DimitryB。如果您一次只显示10条记录,那么20,000行等于2,000页。没有人会打扰所有这些信息。如果不是一两百个,你可以非常安全地减少返回500的记录数。

  2. craigmj's answer提出了一个同样有效的观点,不过我想大大扩展它。通过不在where条款中所有索引 select中的所有内容保证 a table access by index rowid 。这意味着,对于从最里面的select返回的每一行,您已从索引中提取,您也会重新访问该表。我知道这会是你当前桌面上一个非常大的索引。

  3. 您似乎正在做一个比需要更多的子选择。您可以在主select中输入您的订单。您还在别名之上的两个级别引用别名a。虽然这可能不会导致任何问题,但有点令人困惑,您最终会在rnum列中找到两次。

  4. 100米行正朝向光谱的端。任何人都可能无法翻阅所有这些内容,而且从您的评论中,他们并未在报告中全部使用。虽然表是分区的,这将有很大帮助,但Oracle仍然需要确定要使用的分区。

  5. 从第5点开始,您按月对表进行了分区,但只选择了3天的数据。我同意这是明智的,但是如果你只需要几个月的数据,那么很容易可以访问,那么对于一个较小的表,白天可能值得分区吗?你提到2,300个报告使用这个表;如果他们都花了5分钟,那么就应该考虑采用诸如此类的激烈方法。

  6. 这有点可怜,但url是否需要1024个字符?

  7. 这些要点有效归结为我的主要建议; 减小表的大小。 然后,尽一切可能删除Oracle需要做的任何额外工作;甚至将大部分表格变成索引。

    所以,步骤将是:

    • 将超过一个月的所有内容归档到一个单独的表中。每天坚持这样做。

    • 按天分区较小的表格。

    • 如果您只需要几天的数据,那么创建一个materialized view肯定是值得考虑的,订单已经到位。

    • 在这个较小的新表上使用“optimal”索引;所以你根本不需要实际触摸桌子。我把倒置的逗号放在最佳状态,因为这总是有争议的,我的建议可能稍微不正确但通常在where, group by, order by and select clauses所有列的顺序和< / strong>以降低选择性的顺序。这会使您的索引类似于"5mintime", username, category, domain, appid, url, bytes

    • 尽可能使索引唯一。

    • 确保您的查询没有执行任何不需要的操作以减少工作量。这包括返回人类可读行数。我会像这样重写它:


    SELECT time, url, bytes, rownum as rnum
      FROM ( SELECT time, url, bytes, rowum as rnum 
               FROM ( SELECT TO_CHAR ("5mintime",'YYYY-MM-DD HH24:MI:SS') AS time
                           , url
                           , bytes 
                        FROM available_web_details  
                       WHERE "5mintime" BETWEEN to_timestamp('2012-02-10 00:00:00','YYYY-MM-DD HH24:MI:SS')
                                AND to_timestamp('2012-02-13 23:59:59','YYYY-MM-DD HH24:MI:SS')
                         AND username = 'asha1328874833' 
                         AND category = 'None240001'
                         AND domain = '1328874833.vmware.com.'
                         AND appid IN ('216.198.204.192id',...) 
                       ORDER BY "5mintime" DESC
                             )
             WHERE rownum <= 10) 
     WHERE rnum > 0
    

    希望这一切都有意义;在一天结束时,您的报告在服务器上完成的工作量以及您希望它们运行的​​速度将决定您想要做什么。

答案 1 :(得分:0)

您是否尝试过将订单从时间更改为5分钟,我很确定您在转换值并对其进行排序时没有使用索引。

答案 2 :(得分:0)

你有5分钟的索引,所以按顺序排序,然后在最外面的选择中进行TO_CHAR转换。您还可以考虑在WHERE子句中使用的其他字段进行索引。

另一个想法是查看Oracle正在使用的查询计划,以了解它是如何解决问题的。这是关于Oracle explain planhttp://www.adp-gmbh.ch/ora/explainplan.html

的一个很好的教程