【问题标题】:Subquery slows my Query down子查询减慢我的查询速度
【发布时间】:2017-12-24 22:05:30
【问题描述】:

鉴于:

  • 带有事件的视图DataView。事件与时间戳Inst 和数据列一起存储。

  • 事件分为特定的班次或间隔,从每天 06:00、14:00 和 20:00 开始。

  • View DataView 每班拥有数百个事件,积压数年。

  • 当前班次的开始时间戳存储在表CurrShift 中。带有该信息的列标记为StartTime 该表仅包含带有该信息的一行。

目标:

从当前班次中检索所有事件,即DataView.Inst >= CurrShift.StartTime

解决方案但速度慢:

SELECT *
FROM DataTable
WHERE Inst >= (SELECT StartTime FROM CurrShift);

查询运行时间超过 30 分钟。这对于给定的应用程序是不可接受的。

查询更快,但仅限固定日期

SELECT *
FROM Data_Table
WHERE Inst >= TO_DATE('19.07.2017 14:00:00');

此查询在 2.0 秒内运行。对于给定的应用程序来说足够快。

问题:

  1. 为什么第一个解决方案比第二个解决方案慢?我猜想子查询是针对DataView 的每一行执行的,但希望优化器能解决这个问题。

  2. 是否有更高效/更好的方法来执行第一个查询?使用CurrShift.StartTime 比在应用程序中计算时间戳更方便。

@Robbie Toyota:是的,InstStartTime 在两个表中都绝对属于 date 类型。

@dnoeth: 1.) 子查询只返回一行。该表仅包含当前班次的开始和结束时间戳及其名称。 2.) 无论如何我尝试了 MAX(..),但没有成功。

解释计划 我按照 Robbie 的建议执行了解释计划,并看到计划中的巨大差异。但我不知道如何处理这些信息:

慢查询:

SELECTED STATEMENT
  HASH JOIN RIGHT OUTER
    TABLE ACCESS FULL
    HASH JOIN RIGHT OUTER
      TABLE ACCESS FULL
      HASH JOIN RIGHT OUTER
        TABLE ACCESS FULL
  ...

快速查询:

SELECTED STATEMENT
  NESTED LOOPS OUTER
    NESTED LOOPS OUTER
      NESTED LOOPS OUTER
...

具有完全不同的引用表,以及用于散列的巨大 CPU 占用。

【问题讨论】:

  • InstFROM 是时间戳吗?我不在 Oracle 中工作(我从 sql 标记来到这里),但如果 InstFROM 与 Inst 的数据类型不同,则 Inst 上的索引可能不会被使用。
  • 标量子查询应该返回一行(否则无论如何都会失败),您可能需要 top apply MAX:WHERE Inst >= (SELECT MAX(Inst) FROM Time_Table)。但我怀疑这是你真正想要/需要的。
  • 好吧,你知道Time_Table 中只有一行,优化器没有(除非它是DUAL)。试试 MAX。
  • 你用EXPLAIN PLAN检查查询计划了吗?
  • 发布解释计划的内脏 sn-ps 是毫无意义的练习。它们没有告诉我们任何信息,因此我们无法使用它们来解释为什么您的两个查询具有如此不同的执行配置文件。性能调优只关注细节。如果您无法发布我们需要的详细信息(出于公司机密原因或其他原因),您应该删除此问题。

标签: sql oracle performance oracle10g query-optimization


【解决方案1】:

尽量避免使用以下子查询:

DECLARE t Time_Table.Inst%TYPE;
SELECT Inst INTO t FROM Time_Table;
SELECT * FROM Data_Table WHERE Inst >= t;

【讨论】:

  • 这不是 Oracle 语法。该问题标记为[oracle],因此答案需要提出Oracle 语法。请注意,[sql] 仅表示通用 SQL 查询;与 MS SQL Server 相关的问题标记为 [sql-server]
  • 已更新。我的错。
【解决方案2】:

这解决了问题 2。)但它有点混乱。 原始快速解决方案中的 TO_DATE() 可以替换为 SYSDATE,而不会造成任何明显的性能损失。

回顾:

  • 带有 SYSDATE 的 WHERE 子句运行速度相当快

  • 移出 SYSDATE 的起始时间戳很难计算

  • 有一个表提供该开始时间戳

  • 在该表中使用子查询的 WHERE 子句速度很慢

解决方案:

  • 使用带有SYSDATE的WHERE子句将原始数据减少到当前日期

  • 使用 WHERE 子句和子查询将该子集过滤到 start-timestamp 表中速度很快,因为它只是原始数据的一小部分

代码:

SELECT *
FROM (
  SELECT *
  FROM Data_Table
  WHERE Inst >= SYSDATE - 1;
)
WHERE Inst >= (SELECT StartTime FROM CurrShift);

执行时间大约是问题中快速解决方案的 1.5 倍。

关于问题 1:

我还是不知道为什么子查询这么慢。但我学会了更好地解释解释计划。该视图是如此复杂且嵌套很深,以至于从那里开始解释计划是一项艰巨的工作。有趣的事实:解释计划预计查询将运行 9 小时。

【讨论】:

    猜你喜欢
    • 2011-10-04
    • 2015-11-27
    • 1970-01-01
    • 2013-10-15
    • 2019-11-22
    • 2017-12-02
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多