2012-07-11 108 views
3

我希望以下查询的一些指导。我们列出了实验列表以及它们当前的进度状态(为了简单起见,我已将状态缩减为4种,但我们的数据中有10种不同的状态)。我需要最终返回所有未完成实验的当前状态列表。选择最近的行,优化(Oracle SQL)

给定一个表exp_status,

Experiment | ID  | Status 
---------------------------- 
    A  | 1 | Starting 
    A  | 2 | Working On It 
    B  | 3 | Starting 
    B  | 4 | Working On It 
    B  | 5 | Finished Type I 
    C  | 6 | Starting 
    D  | 7 | Starting 
    D  | 8 | Working On It 
    D  | 9 | Finished Type II 
    E  | 10 | Starting 
    E  | 11 | Working On It 
    F  | 12 | Starting 
    G  | 13 | Starting 
    H  | 14 | Starting 
    H  | 15 | Working On It 
    H  | 16 | Finished Type II 

所需的结果集:

Experiment | ID | Status 
---------------------------- 
    A  | 2 | Working On It 
    C  | 6 | Starting 
    E  | 11 | Working On It 
    F  | 12 | Starting 
    G  | 13 | Starting 

最新的ID号将对应于最新状态。

现在,我在150秒内执行的当前代码。

SELECT * 
    FROM 
      (SELECT Experiment, ID, Status, 
      row_number() over (partition by Experiment 
      order by ID desc) as rn 
      FROM exp_status) 
    WHERE rn = 1 
    AND status NOT LIKE ('Finished%') 

事情是,这段代码浪费了时间。结果集是从3.9百万张表中拉出的45,000行。这是因为大多数实验都处于完成状态。代码通过并命令所有这些代码只在最后过滤完成。表中大约95%的实验都处于完成阶段。我无法弄清楚如何使查询首先挑选出那个实验没有“成品”的所有实验和状态。我尝试了以下,但表现非常慢。

SELECT * 
FROM exp_status 
WHERE experiment NOT IN 
(
    SELECT experiment 
    FROM exp_status 
    WHERE status LIKE ('Finished%') 
) 

任何帮助将不胜感激!

+0

如果status列被索引,那么可能值得使用'row_number()'函数将'status not like ...'条件移入内部select。 – 2012-07-12 11:47:26

回答

2

根据您的要求,我认为您当前的查询与row_number()是最有效的可能之一。这个查询花费的时间不是因为它必须对数据进行排序,而是因为有太多数据要读取(额外的cpu时间与获取时间相比可以忽略不计)。此外,第一个查询使得FULL SCAN成为读取大量数据的最佳方式。

如果您想提高性能,您需要找到一种方法来减少很多行。第二个查询的方向不正确:

  1. 内部查询很可能是一个完整的扫描,因为'已完成'的行会遍布整个表,并可能占所有行的很大比例。
  2. 外部查询也将是一个完整的扫描和一个不错的ANTI-HASH JOIN,它应该比45k *(每个实验的状态更改次数)非唯一索引扫描更快。

所以第二个查询似乎至少有两倍的读取次数(加上一个连接)。

如果你想真正提高性能,我认为你需要改变设计。

您可以创建一个活动实验表并加入到此表中。您可以将此表维护为物化视图或修改插入实验状态的代码。您可以继续并将最后一个状态存储在此表中。保持这种“最后的状态”可能会带来额外的负担,但这可以通过改进的性能来证明。

+0

感谢您的提示 - 我可能会坚持使用row_number()方法! – User 2012-07-11 16:12:30

+0

主要杀手是在状态不LIKE'Finished%''查询的一部分。在执行“row_number()”之前,通过过滤**未完成的**实验来减少查询的数据集可能会有帮助。 – 2014-01-15 16:52:47

+0

@justCallMeBiru您无法知道哪些实验已完成,哪些不在内部查询中,这就是为什么我们需要外部查询。 – 2014-01-15 17:23:58

1

考虑status

www.orafaq.com/wiki/Partitioning_FAQ

你的分区表你也可以创建物化视图,以避免重新计算你的聚合,如果这些类型的查询频繁。

您能否提供您的查询的执行计划。如果没有这些,很难知道它是这么长时间的确切原因

+0

分区似乎很有趣。我没有执行计划书,但是,正如文森特在上面所说的,很长一段时间可能只是由于桌子的大小。 – User 2012-07-11 16:14:53

0

您可以通过使用该变种略有提高你的第一个查询:

select experiment 
    , max(id) id 
    , max(status) keep (dense_rank last order by id) status 
    from exp_status 
group by experiment 
having max(status) keep (dense_rank last order by id) not like 'Finished%' 

如果你比较的计划,你会发现一步less

Regards,
Rob。