Oracle:连接两个快速、不相关的查询导致查询缓慢

Posted

技术标签:

【中文标题】Oracle:连接两个快速、不相关的查询导致查询缓慢【英文标题】:Oracle: join of two fast, non-correlated queries causing slow query 【发布时间】:2018-06-28 10:21:21 【问题描述】:

为什么两个快速、简单的查询的连接需要这么长时间才能运行?

如何强制子查询只运行一次?

问题

我有一个查询连接到一个 不相关 子查询(为了便于说明,我已经稍微简化了这个示例),它需要 1684 秒才能返回 3450 行数据。

但是,子查询在单独运行时会在

如果它们不相关,则子查询应该只运行一次,总执行时间约为 8-9 秒,不是吗?

如何强制子查询只运行一次并缓存结果?

查询本身:

带有子查询的完整查询(1684 秒(28 分钟)内 3450 行)

SELECT
                  table_1.a, 
                  table_3.b, 
                  table_4.c, 
                  subquery_result.c

FROM              table_1
LEFT OUTER JOIN   table_2 on (table_2.x = table_1.y)
LEFT OUTER JOIN   table_3 on (table_3.x = table_2.y)
LEFT OUTER JOIN   table_4 on (table_4.x = table_2.z)
LEFT OUTER JOIN   table_5 on (table_5.x = table_4.y)
LEFT OUTER JOIN 

                  (
                    SELECT 
                                      view_1.a, 
                                      view_2.b, 
                                      resultOfLongCaseStatement c

                    FROM              view_1 
                    LEFT OUTER JOIN   view_2 on (view_1.x = view_2.y)
                  ) subquery_result   on (table_1.x = subquery_result.b)

WHERE             table_3.val = 'SOMEVAL'
AND               table_2.val in ('VAL', 'OTHERVAL')
AND               table_5.val is not null
AND               subquery_result.a not in ('blah', 'blahh'); 

自己的子查询(在

SELECT 
                  view_1.a, 
                  view_2.b, 
                  resultOfLongCaseStatement

FROM              view_1 
LEFT OUTER JOIN   view_2 on (view_1.x = view_2.y)

没有子查询的整体查询(4.4秒内返回3504行):

SELECT
                  table_1.a, 
                  table_3.b, 
                  table_4.c, 
                  --subquery_result.c

FROM              table_1
LEFT OUTER JOIN   table_2 on (table_2.x = table_1.y)
LEFT OUTER JOIN   table_3 on (table_3.x = table_2.y)
LEFT OUTER JOIN   table_4 on (table_4.x = table_2.z)
LEFT OUTER JOIN   table_5 on (table_5.x = table_4.y)

WHERE             table_3.val = 'SOMEVAL'
AND               table_2.val in ('VAL', 'OTHERVAL')
AND               table_5.val is not null
AND               subquery_result.a not in ('blah', 'blahh'); 

使用“with”子句重写整个查询 - 与运行上述整体查询的性能相同:

WITH subquery_result as 
                (
                    SELECT 
                                      view_1.a, 
                                      view_2.b, 
                                      resultOfLongCaseStatement c

                    FROM              view_1 
                    LEFT OUTER JOIN   view_2 on (view_1.x = view_2.y)
                )

SELECT
                  table_1.a, 
                  table_3.b, 
                  table_4.c, 
                  subquery_result.c

FROM              table_1
LEFT OUTER JOIN   table_2 on (table_2.x = table_1.y)
LEFT OUTER JOIN   table_3 on (table_3.x = table_2.y)
LEFT OUTER JOIN   table_4 on (table_4.x = table_2.z)
LEFT OUTER JOIN   table_5 on (table_5.x = table_4.y)
LEFT OUTER JOIN   subquery_result   on (table_1.x = subquery_result.b)

WHERE             table_3.val = 'SOMEVAL'
AND               table_2.val in ('VAL', 'OTHERVAL')
AND               table_5.val is not null
AND               subquery_result.a not in ('blah', 'blahh'); 

为什么 2 秒和 5 秒查询的连接会导致 23 分钟的查询时间?

子查询是否为整个查询生成的每一行运行一次?我怎么能强迫它不这样做?

我尝试过的:

将子查询重写为 'with' 语句 - 执行时间仍接近 28 分钟。

作为一个实验,为了确保连接不是问题,我尝试将子查询的输出放入一个表中并加入:这使得查询在几秒钟内运行 - 但我无法使用这种方法解决我的问题

稍微重写子查询并首次运行它们,以防我看到的快速结果只是被缓存 - 子查询的执行时间和没有子查询的整体查询仍然相同 - 许多比两者结合起来快数百倍。

【问题讨论】:

在 WHERE 子句中带有右侧表条件的 LEFT JOIN 返回常规 INNER JOIN 结果。 @jarlh - 你就在那里 - 我只是想在我的例子中尽可能少地改变。不管使用什么连接,子查询仍然是不相关的,不是吗?您认为删除连接右侧的 where 条件会显着影响性能吗? @jarlh ... 好的,如果我删除所有 where 子句,它会在 14 秒内运行......嗯?为什么? 【参考方案1】:

事实证明,我正在查询的表中的一列(例如,table_2.val 是全新的 - 它没有索引,优化器对该列的统计信息为 0。

在选择该列时这不是问题 - 但是当我在 WHERE 子句中包含该列时,优化器显然非常困难 - 因为它似乎正在运行子查询,就好像它是相关的(在总体中每个结果一次)查询) - 即使在子查询中的任何地方都没有提到或引用 table_2。

快速解决方案是:一次注释掉 where 子句中的每个谓词,看看哪个谓词导致性能大幅提升。

我相信(但尚未测试)更新我的“table_2”上的统计数据也可以长期解决这个问题。

【讨论】:

以上是关于Oracle:连接两个快速、不相关的查询导致查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章

Oracle: 两个SQL语句查询数量不一致。

MySQL连接两个不相关查询的结果

Oracle 存储函数与查询中的联接

oracle查询未释放连接的sql

oracle数据库用了定时器查询数据库,每一分钟查询一次,一段时间后导致数据库最大连接数太大

Oracle Left Join 导致单行子查询返回多行错误