【问题标题】:Using empty OVER() clause in subquery在子查询中使用空的 OVER() 子句
【发布时间】:2019-07-22 08:28:29
【问题描述】:

我有一个使用相关子查询的 Oracle SQL 查询:

Q1 P>

SELECT t1.id,
       t3.code,
       t3.processed_date,
       (t1.total / t2.rate) 
FROM table1 t1
         JOIN table2 t2 ON t2.code= t1.code
         JOIN table3 t3 ON t3.id = t1.id
         JOIN table4 t4 ON t4.code = t1.code
    AND t4.type IN ('value1', 'value2', 'value3')
    AND t3.processed_date >= '01 JUL 2019'
    AND t3.processed_date < '22 JUL 2019'
    AND t2.effective_date IN (SELECT max(tc.effective_date)
                                FROM tableCore tc
                                WHERE tc.effective_date <= t3.processed_date
                                  AND t1.code = tc.code)

我已将子查询更改为 Empty OVER() 原因,这显着提高了此查询的性能:

Q2 P>

SELECT t1.id,
       t3.code,
       t3.processed_date,
       (t1.total / t2.rate) 
FROM table1 t1
         JOIN table2 t2 ON t2.code= t1.code
         JOIN table3 t3 ON t3.id = t1.id
         JOIN table4 t4 ON t4.code = t1.code
    AND t4.type IN ('value1', 'value2', 'value3')
    AND t3.processed_date >= '01 JUL 2019'
    AND t3.processed_date < '22 JUL 2019'
    AND t2.effective_date IN (SELECT max(tc.effective_date) OVER () AS ed
                                FROM tableCore tc
                                WHERE tc.effective_date <= t3.processed_date
                                  AND t1.code = tc.code)

新查询返回与原始查询相同的结果集,因此似乎正在运行...,但是为什么解释计划如此不同,它似乎仍然相关,它不是对外部查询了吗?为什么?

我想了解第二个查询中发生了什么。

我想我可以使用row_number() OVER (partition by ...) 以第三种方式重写此查询:

Q3 P>

    SELECT t1.id,
       t3.code,
       t3.processed_date,
       (t1.total / t2.rate),
       ct.*
FROM table1 t1
         JOIN table2 t2 ON t2.code = t1.code
         JOIN table3 t3 ON t3.id = t1.id
         JOIN table4 t4 ON t4.code = t1.code
         JOIN (SELECT ct.*, row_number() OVER (PARTITION BY ct.code ORDER BY ct.effective_date ASC) AS rn
               FROM tablecore ct) ct
              ON t1.code = ct.code
                  AND rn = 1
                  AND ct.effective_date <= t3.processed_date
WHERE t2.effective_date in(ct.effective_date)
  AND t4.type IN ('value1', 'value2', 'value3')
  AND t3.processed_date >= '01 JUL 2019'
  AND t3.processed_date < '22 JUL 2019'
  AND t2.effective_date IN (ct.effective_date);

这个版本似乎也可以,但是比第二个版本慢。

编辑 正如@Christian Q3 指出的那样,将返回不正确的结果

【问题讨论】:

  • 你能提供两个查询的执行计划吗?

标签: sql oracle correlated-subquery analytic-functions


【解决方案1】:

这里有另一个选项供您尝试:

SELECT t1.id,
       t3.code,
       t3.processed_date,
       (t1.total / t2.rate) 
FROM table1 t1
         JOIN table2 t2 ON t2.code= t1.code
         JOIN table3 t3 ON t3.id = t1.id
         JOIN table4 t4 ON t4.code = t1.code
    AND t4.type IN ('value1', 'value2', 'value3')
    AND t3.processed_date >= '01 JUL 2019'
    AND t3.processed_date < '22 JUL 2019'
    AND EXISTS (SELECT 1
                                FROM tableCore tc
                                WHERE tc.effective_date <= t3.processed_date
                                  AND t1.code = tc.code
                                HAVING t2.effective_date = max(tc.effective_date))

对于正确的性能分析,最好有表的大小,以及不同查询的解释计划(如果优化器统计信息准确,还需要检查)。

仅供参考:您使用 row_number() 进行的最后一次查询将不会提供相同的结果,因为将在连接 ct.effective_date &lt;= t3.processed_date 之前评估窗口函数,因此您会错过一些行。

【讨论】:

  • And FYI: Your last query with row_number() will not deliver the same result, as the windowing function will be evaluated before the join ct.effective_date &lt;= t3.processed_date, and therefore you will miss some rows. 我已经测试了最后一个查询,我没有丢失任何行,您确定这会导致问题吗?
  • 对于每个 ct.code,最大的 ct.effective_date 将有 rn = 1。如果此effective_date 大于t3.processed_date,它将不匹配这两个条件。如果您的代码中没有这种情况,那么您将不会丢失任何行(使用当前数据)。但是你确定这永远不会发生吗?那么你需要这个条件ct.effective_date &lt;= t3.processed_date
  • 我现在明白了,谢谢,tableCore ct包含由effective_date 版本化的静态数据,所以ct.effective_date &lt;= t3.processed_date 将始终评估为真,但我认为查询可能会返回错误的版本,因为你指出的原因。您能否确认我的 Q2 将按预期工作,并解释空 Over() 子句正在做什么以使查询变得如此之快?我的 Q2 的解释计划与您提供的查询的解释计划几乎相同。
  • 很难说没有解释计划 :) 我的猜测是,Oracle 在第一个查询中使用嵌套循环(在主查询中的每一行上查找 tableCore) ,而空的 OVER() 则强制 Oracle 提前读取洞 tableCore,在这种情况下是更快的选项。
  • 我必须在发布之前清理解释计划,你的假设是有道理的,我找不到任何资源表明空的 OVER() 子句会以这种方式运行,看起来像对这样的查询非常有用的优化,你会认为我可以在某个地方找到一个例子......谢谢你的帮助:)
【解决方案2】:

当您可以简单地使用= 运算符时,您为什么还要使用IN

SELECT t1.id,
       t3.code,
       t3.processed_date,
       (t1.total / t2.rate) 
FROM table1 t1
         JOIN table2 t2 ON t2.code= t1.code
         JOIN table3 t3 ON t3.id = t1.id
         JOIN table4 t4 ON t4.code = t1.code
    AND t4.type IN ('value1', 'value2', 'value3')
    AND t3.processed_date >= '01 JUL 2019'
    AND t3.processed_date < '22 JUL 2019'
    AND t2.effective_date = (SELECT max(tc.effective_date)
                                FROM tableCore tc
                                WHERE tc.effective_date <= t3.processed_date
                                  AND t1.code = tc.code)

显着的性能提升是因为磁盘 I/O 减少

【讨论】:

  • 原来的查询不是我写的,可能当初写的时候条件比较多,只是在试验解析函数的时候没改。
猜你喜欢
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 2014-08-01
相关资源
最近更新 更多