【问题标题】:Selecting Recent Rows, Optimization (Oracle SQL)选择最近的行,优化 (Oracle SQL)
【发布时间】:2012-07-11 05:37:40
【问题描述】:

我希望对以下查询提供一些指导。我们有一个实验列表及其当前进度状态(为简单起见,我将状态减少到 4 种类型,但我们的数据中有 10 种不同的状态)。我最终需要返回所有未完成实验的当前状态列表。

给定一个表 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%')

问题是,这段代码浪费了时间。结果集是从 390 万个表中提取的 45000 行。这是因为大多数实验都处于完成状态。代码通过并对所有这些进行排序,然后仅在最后过滤掉完成的内容。表中大约 95% 的实验处于完成阶段。我不知道如何让查询首先挑选出所有没有“完成”的实验和状态。我尝试了以下但性能非常慢。

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

任何帮助将不胜感激!

【问题讨论】:

  • 如果状态列被索引,可能值得尝试使用row_number() 函数将status not like ... 条件移动到内部选择中。

标签: sql oracle optimization select filter


【解决方案1】:

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

如果你想提高性能,你需要找到一种方法来读取更少的行。第二个查询没有朝着正确的方向发展:

  1. 内部查询很可能是完整扫描,因为“完成”的行将分布在整个表中,并且可能占所有行的很大一部分。
  2. 外部查询也可能是完整扫描和良好的 ANTI-HASH JOIN,它应该比 45k *(每个实验的状态更改数)非唯一索引扫描更快。

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

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

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

【讨论】:

  • 感谢您的提示 - 我可能会坚持使用 row_number() 方法!
  • 主要的杀手是查询的status not LIKE 'Finished%' 部分。通过在对它进行row_number() 之前过滤未完成实验来减少查询的数据集可能会有所帮助。
  • @justCallMeBiru 你无法知道哪些实验已经完成,哪些不在内部查询中,这就是我们需要外部查询的原因。
【解决方案2】:

考虑通过status对表进行分区

www.orafaq.com/wiki/Partitioning_FAQ

如果这些类型的查询很频繁,您还可以创建物化视图以避免重新计算聚合。

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

【讨论】:

  • 分区似乎很有趣。我没有执行计划特权,但是,就像上面文森特所说的那样,很长的时间可能只是由于表的大小。
【解决方案3】:

您可以使用此变体稍微改进您的第一个查询:

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%'

如果你比较计划,你会发现少了一步

问候,
抢。

【讨论】:

    猜你喜欢
    • 2018-06-12
    • 2018-08-20
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 2020-06-16
    • 2020-01-22
    • 1970-01-01
    • 2015-06-21
    相关资源
    最近更新 更多