选择非常大的表中的最新值

时间:2018-09-07 22:31:41

标签: sql oracle datetime

我是一名操作人员,负责从一个很大的表中提取数据。我不是DBA,因此无法对其进行分区或更改索引。表有将近十亿个条目,未分区,并且可能被索引为“更好”。我需要两个字段,我们将其称为mod_date和obj_id(对mod_date进行了索引)。编辑:我还为“客户”添加了一个过滤器,该过滤器在我的解释计划的屏幕快照中已经模糊掉。

我的数据: 在近十亿行的组中,我们要在几年内查询的obj_id值少于10,000个(少数甚至可能为NULL)。某些<10k obj_id(可能在1,000至2,500之间)每个都有超过1000万个mod_date值。当obj_id具有数百万个mod_date时,每个obj_id都需要几分钟才能使用MAX(mod_date)进行扫描和排序。完整的结果集需要花费超过12个小时的时间进行查询,并且没有任何“问题”(锁定,笔记本电脑未插电等)使它无法完成。即使返回了前50行,我们仍然需要将其导出到Excel中……它只有大约8,000行有2列,但我们永远都无法做到这一点。

这是一个简化的查询,如果它是一个小表,我会使用它:

select MAX(trunc(mod_date,'dd')) as last_modified_date, obj_id
from my_table
where client = 'client_name'
and obj_type_id = 12
group by obj_id;

基数为317917582,“费用”为12783449

explain plan

问题: 问题是给定当前索引,使用如此大的未分区表的查询速度。我看到的关于“最近日期”的所有其他答案都倾向于使用MAX,可能与FIRST_VALUE结合使用,后者似乎需要对所有行进行全面扫描才能对其进行排序,然后确定哪个是最新的。 / p>

我希望有一种避免这种情况的方法,以加快结果的速度。看来Oracle(我正在使用Oracle SQL开发人员)应该能够获取obj_id,从“ now”开始查找最新的mod_date行,然后向后工作,并在找到任何mod_date值后立即继续操作…因为是约会。有办法吗?

即使具有如此大的表,具有少于10,000个mod_dates的obj_ids也可以非常快(几秒钟或更短)返回MAX(mod_date)。我们遇到的问题是,如果我可以让Oracle开始首先查看最新的mod_date(超过1000万个)的obj_id,则扫描和排序的时间最长,而它们“应该”最快的时间……因为快速找到最近的日期并继续前进!

2 个答案:

答案 0 :(得分:2)

首先,我要说一个普遍的误解,即为了使查询运行更快,您需要一个索引(或更好的索引)。当您提取超过10%的数据时,全表扫描才有意义(粗略估计,取决于多块读取计数,块大小等)。

我的建议是设置一个物化视图(MY_MV或其他),该视图简单地通过查询(对所有ID)进行分组。如果您需要将ID限制为1万个子集,则只需确保完全扫描表格即可(检查说明计划)。您可以根据需要添加完整提示(从big_table t ...中选择/ * + full(t)* / ..)

然后做:

dbms_mview.refresh('MY_MV','C',atomic_refresh=>false);

就这样。客户端只返回前x行就没有问题,当您提取所有内容时,它将重新运行整个查询(ugh)。全扫描也更容易跟踪长选项(例如,如果您在索引上执行嵌套循环,则很难告诉您取得了什么进展)。

完成后,将整个MV表转储到文件或任何您需要的文件中。

答案 1 :(得分:2)

我认为tbone正确。或者,如果您无权按照他的建议创建实例化视图,则可以在数据库服务器上创建一个Shell脚本,以通过SQL * Plus将查询运行并将输出spool存储到文件中。然后,使用nohup运行该脚本,您不必担心笔记本电脑被关闭等问题。

但我想解释一下您的评论:

  

Oracle应该能够获取obj_id,从“ now”开始查找最新的mod_date行,然后向后工作,并在找到任何mod_date值后继续前进……因为它是一个日期。有办法吗?

给定列出的索引,这对于Oracle运行查询是一种可怕的方式。让我们逐步解决...

obj_id上没有索引,因此Oracle需要进行全表扫描以确保它获取所有不同的obj_id值。

因此,它启动FTS并找到obj_id101。然后它说:“我需要max(mod_date) for 101 ...啊哈!我有索引!”因此,它执行反向索引扫描。对于索引中的每个条目,它都会从表中查找行,并检查它是否为obj_id101。如果obj_id最近被更新,则很好,因为我们找到了它并停止早。但是,如果obj_id长时间没有更新,我们必须读取许多索引条目,并分别访问表行以执行检查。

在最坏的情况下-如果obj_id是您提到的少数几个max(mod_date)将是NULL的地方之一,我们将使用索引查找您的每个单行具有非空mod_date的表。

如果只执行一次,那么执行这么多索引查找将是一个糟糕的计划,但是您正在谈论的是针对几个旧的或从未更新的obj_id值进行此操作。

无论如何,这都是学术性的。没有Oracle查询计划会以这种方式运行查询。这是有充分理由的。

没有更好的索引编制,您只会在一次全表扫描中无法改善。