选择最近的行,优化(Oracle SQL)

选择最近的行,优化(Oracle SQL),sql,oracle,optimization,select,filter,Sql,Oracle,Optimization,Select,Filter,我希望能就以下问题提供一些指导。我们有一个实验列表和它们当前的进展状态(为了简单起见,我将状态减少到了4种,但我们的数据中有10种不同的状态)。我需要最终返回所有未完成实验的当前状态列表 给定一个表exp\u状态 Experiment | ID | Status ---------------------------- A | 1 | Starting A | 2 | Working On It B | 3

我希望能就以下问题提供一些指导。我们有一个实验列表和它们当前的进展状态(为了简单起见,我将状态减少到了4种,但我们的数据中有10种不同的状态)。我需要最终返回所有未完成实验的当前状态列表

给定一个表exp\u状态

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%')
问题是,这个代码浪费时间。结果集是从一个390万行的表中抽取45000行。这是因为大多数实验都处于完成状态。代码将遍历并排序所有的代码,然后只过滤掉最后完成的代码。表中约95%的实验处于完成阶段。我不知道如何使查询首先挑出所有的实验和状态,其中没有一个“完成”的实验。我尝试了以下方法,但效果很慢

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

任何帮助都将不胜感激

考虑到您的需求,我认为您当前使用
行号()进行的查询是最有效的查询之一。此查询需要时间,不是因为它必须对数据进行排序,而是因为首先要读取的数据太多(与提取时间相比,额外的cpu时间可以忽略不计)。此外,第一个查询进行完全扫描,这是读取大量数据的最佳方式

如果要提高性能,需要找到一种方法来读取更少的行。第二个查询的方向不对:

  • 内部查询可能是完全扫描,因为“完成”行将分布在整个表中,并且可能代表所有行的很大一部分
  • 外部查询也可能是一个完整扫描和一个很好的反散列连接,它应该比45k*(每个实验的状态更改数)非唯一索引扫描快
  • 因此,第二个查询的读取次数似乎至少是两倍(加上一个连接)

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


    例如,您可以构建一个活动实验表并加入该表。您可以将该表维护为物化视图,或者对插入实验状态的代码进行修改。您可以进一步将最后一个状态存储在此表中。维护此“最后状态”可能是一个额外的负担,但这可以通过改进的性能来证明。

    考虑按
    状态对表进行分区。

    您还可以创建物化视图,以避免在此类查询频繁时必须重新计算聚合


    您能否提供查询的执行计划。如果没有这些,很难知道花费这么长时间的确切原因,您可以使用以下变量稍微改进您的第一个查询:

    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%'
    
    如果你比较这些计划,你会发现少了一步

    问候,

    Rob.

    谢谢你的提示——那么我可能会坚持使用row_number()方法!主要杀戮的状态与查询的“Finished%”部分不同。在对数据集执行
    行编号()
    之前,通过过滤未完成的实验来减少查询的数据集可能会有所帮助。@justCallMeBiru您无法知道哪些实验已完成,哪些不在内部查询中,这就是我们需要外部查询的原因。分区似乎很有趣。我没有执行计划特权,但是,正如Vincent在上面所说的,很长时间可能是由于表太大。如果status列被索引,可能值得尝试使用
    row\u number()
    函数将
    status not like…
    条件移动到内部select中。