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:连接两个快速、不相关的查询导致查询缓慢的主要内容,如果未能解决你的问题,请参考以下文章