【发布时间】:2020-03-19 01:43:47
【问题描述】:
简短的故事来解释我是如何陷入这个查询性能混乱的:
有人交给我一个旧的 Oracle 数据库来修复和维护。
数据模型设计得很糟糕,没有远见。
数据库状况糟糕,执行速度极慢。
当然,这个数据库已经超过 6 年没有调整或修改了......
我从这个数据库中的许多“快照”表之一开始。
(原始开发人员没有正确跟踪历史记录,而是将记录的快照计划复制并存储在各地的其他表中。因此您必须查询这些快照表以获取历史分析。)
此表有大约 100 列,但我们并不关心其中的大部分。
然而,当我几天前开始处理这个问题时,这个表有只有一个索引:ID 列的非唯一索引,根本没有主键约束。
这是一个快照表,这意味着它包含来自不同时间点的行的历史副本。
例如,9/20/19 上的第 #12345 行,以及 9/30/19 上的第 #12345 行再次相同
所以,唉,ID 列必须允许重复值。
所以我想,第一步:在ID 和snapshot_date 上创建一个复合主键约束,以创建一个正确的唯一标识符。
我正在尝试构建的分析查询是该应用程序中已经使用的许多不同的预先存在的查询的科学怪人。
它看起来像垃圾,因为这是我必须处理的......
select efh.snapshot_date,
max(efhp.snapshot_date) as previous_snapshot_date,
substr(efh.edge_vp,1,instr(efh.edge_vp,'@oracle.com')-1) as edge_vp,
substr(efh.edge_rm,1,instr(efh.edge_rm,'@oracle.com')-1) as edge_rm,
sum(case when efh.oppty_status = 'Open' then NVL(efh.ARR_FORECAST, 0) else 0 end) as forecast,
sum(case when efh.oppty_status = 'Open' then NVL(efh.ARR_BEST,0) else 0 end) as best,
sum(case when efh.oppty_status = 'Won' then NVL(efh.ARR,0) else 0 end) as closed,
sum(case when efh.oppty_status = 'Open' then nvl(efh.ARR_PIPELINE,0) else 0 end) as pipeline,
sum(case when efh.oppty_status = 'Open' then NVL(efh.ARR_BEST,0) else 0 end) +
sum(case when efh.oppty_status = 'Open' then nvl(efh.ARR_PIPELINE,0) else 0 end) as pipe_best,
sum(case when efh.oppty_status = 'Won' then efh.ARR else 0 end) +
sum(case when efh.oppty_status = 'Open' then NVL(efh.ARR_FORECAST,0) else 0 end) as closed_forecast
from edge_forecast_hist efh
left join edge_forecast_hist efhp on efhp.edge_vp = efh.edge_vp and efhp.edge_rm = efh.edge_rm and efhp.snapshot_date < efh.snapshot_date
where efh.snapshot_date >= TRUNC(sysdate) - INTERVAL '70' DAY
and efh.edge_asm != 'REDACTED'
and efh.oppty_status in ('Open', 'Won')
group by efh.snapshot_date,
substr(efh.edge_vp,1,instr(efh.edge_vp,'@oracle.com')-1),
substr(efh.edge_rm,1,instr(efh.edge_rm,'@oracle.com')-1)
order by 1, 2, 3, 4
一开始,这个查询需要大约 5 分钟来执行。
在我创建主键以及此查询中引用的每一列的索引后,执行时间下降到约 4 分钟,这绝对是一个改进,但不如我预期的那么好。
(这个查询只返回几百行。)
当我尝试explain 这个查询时,我注意到实际上只有少数索引被使用。
(请参见上面的屏幕截图中的三个复选标记,指示正在使用哪些索引。)
执行计划中有一些令人不安的语言,
比如NESTED LOOPS和TABLE ACCESS(表扫描??)
这是一个糟糕的数据库,我是 Oracle 的新手,我并不完全了解执行计划中所有内容的细微差别。
这里的瓶颈似乎是什么,以及如何可能我减轻它?
想到的一些想法:
- 自连接(这应该不是什么大问题,任何现代数据库都应该能够轻松处理)
-
每列内嵌
case语句,在选择列表中,内部聚合函数 (SUM)
如果真的有帮助,我可以做几个小时的工作,尝试将那些 case 语句分开。
(即在多个子查询中计算结果,将case 条件移到where 子句中。)
但我也认为 Oracle 足够聪明,可以找到最有效的执行计划,而不需要被灌输。
换句话说,我不想做所有这些工作,只是为了得到完全相同的执行计划。
我需要知道真正的问题是什么,这样我才能制定一个有针对性的解决方案来正确解决根本原因。
【问题讨论】:
-
样本数据和期望的结果会有很大帮助。查询应该做什么?
-
@GordonLinoff 你好。 XD 感谢您的帮助和耐心。让我看看我是否可以为你收集结果。查询有效,我认为结果是准确的——只是速度很慢。给我几分钟来执行查询,我会发布一些结果。 (我必须编辑姓名和电子邮件地址等,所以可能需要一点时间。)
-
edge_vp和edge_rm中的值是什么? -
@Nick 这些是
VARCHAR列,包含短文本值(大约 25 个字符,给或取)。其余列应该都是数字类型,主要是整数。 -
@oracle.com@oracle.com之后之后有什么东西吗?
标签: sql oracle performance query-performance sqlperformance