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

时间:2018-06-28 10:21:22

标签: sql oracle subquery

为什么将两个快速,简单的查询合并需要这么长时间才能运行?

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

问题

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

但是,子查询单独运行时,其运行时间不到2秒,如果删除子查询,整个查询的运行时间将少于5秒。

如果它们不相关,则子查询应该只运行一次,总执行时间约为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'); 
    
  • 子查询本身(在<2秒内返回3812行)

    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分钟。

  • 作为一个实验,为确保联接不是问题,我尝试将子查询的输出放入表中并进行联接:这使得查询仅需几秒钟即可运行-但是我不能使用这种方法来解决我的问题

  • 略微重写子查询并首次运行它们,以防万一我看到的快速结果只是被缓存了-子查询的执行时间和不带子查询的整体查询的执行时间仍然相同-很多比两者结合在一起要快数百倍。

1 个答案:

答案 0 :(得分:2)

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

选择该列时这不是问题-但是当我将该列包括在WHERE子句中时,优化器显然很费劲-因为它似乎正在运行子查询,就好像它是相关的一样(一次在整个结果中查询)-即使未在子查询中的任何地方提及或引用table_2。

一种快速的解决方案是:一次注释掉where子句中的每个谓词,然后查看哪个谓词带来了巨大的性能提升。

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