我有下面的查询,它在执行后从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个月的范围分区表。
答案 0 :(得分:2)
让我们从免责声明开始:如果您不删除order by
或开始使用临时表,则执行时间永远不会减少到4秒。第二个免责声明:我犯了一个大错误,我现在正在纠正;我花了很长时间才意识到这一点。谢谢Alex Poole。
我的观点(我总是有几个)。
评论中提出了一些非常有效的观点,特别是DimitryB。如果您一次只显示10条记录,那么20,000行等于2,000页。没有人会打扰所有这些信息。如果不是一两百个,你可以非常安全地减少返回500的记录数。
craigmj's answer提出了一个同样有效的观点,不过我想大大扩展它。通过不在where
条款中所有索引和 select
中的所有内容保证 a table access by index rowid
。这意味着,对于从最里面的select
返回的每一行,您已从索引中提取,您也会重新访问该表。我知道这会是你当前桌面上一个非常大的索引。
您似乎正在做一个比需要更多的子选择。您可以在主select
中输入您的订单。您还在别名之上的两个级别引用别名a
。虽然这可能不会导致任何问题,但有点令人困惑,您最终会在rnum
列中找到两次。
100米行正朝向光谱的大端。任何人都可能无法翻阅所有这些内容,而且从您的评论中,他们并未在报告中全部使用。虽然表是分区的,这将有很大帮助,但Oracle仍然需要确定要使用的分区。
从第5点开始,您按月对表进行了分区,但只选择了3天的数据。我同意这是明智的,但是如果你只需要几个月的数据,那么很容易可以访问,那么对于一个较小的表,白天可能值得分区吗?你提到2,300个报告使用这个表;如果他们都花了5分钟,那么就应该考虑采用诸如此类的激烈方法。
这有点可怜,但url
是否需要1024个字符?
这些要点有效归结为我的主要建议; 减小表的大小。 然后,尽一切可能删除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 plan
:http://www.adp-gmbh.ch/ora/explainplan.html