Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Desempenho lento para fatoração de subconsulta profundamente aninhada (CTE)


Q1:Parece que não há nada sobre o tempo de cálculo, apenas bug no algoritmo do otimizador que o torna louco ao calcular o melhor plano de execução.

Q2:Existem vários bugs conhecidos e corrigidos no Oracle 11.X.0.X relacionados à otimização de consultas aninhadas e fatoração de consultas. Mas é muito difícil encontrar um problema concreto.

Q3:Há dois não documentados dicas:materialize e inline mas nenhum deles funciona para mim enquanto eu tentei o seu exemplo. É possível que algumas mudanças na configuração do servidor ou atualização para 11.2.0.3 possam aumentar o limite de with aninhado cláusulas:para mim (em 11.2.0.3 Win7/x86) seu exemplo funciona bem, mas aumentar o número de tabelas aninhadas para 30 trava uma sessão.

A solução alternativa pode ter esta aparência:
select k from (
select k, avg(k) over (partition by null) k_avg from ( --t16
  select k, avg(k) over (partition by null) k_avg from ( --t15
    select k, avg(k) over (partition by null) k_avg from ( --t14
      select k, avg(k) over (partition by null) k_avg from ( --t13
        select k, avg(k) over (partition by null) k_avg from ( --t12
          select k, avg(k) over (partition by null) k_avg from ( --t11
            select k, avg(k) over (partition by null) k_avg from ( --t10
              select k, avg(k) over (partition by null) k_avg from ( --t9
                select k, avg(k) over (partition by null) k_avg from ( --t8
                  select k, avg(k) over (partition by null) k_avg from ( --t7
                    select k, avg(k) over (partition by null) k_avg from ( --t6
                      select k, avg(k) over (partition by null) k_avg from ( --t5
                        select k, avg(k) over (partition by null) k_avg from ( --t4
                          select k, avg(k) over (partition by null) k_avg from ( --t3
                            select k, avg(k) over (partition by null) k_avg from ( --t2
                              select k, avg(k) over (partition by null) k_avg from ( -- t1
                                select k, avg(k) over (partition by null) k_avg from (select 0 as k from dual) t0
                              ) where k >= k_avg
                            ) where k >= k_avg
                          ) where k >= k_avg
                        ) where k >= k_avg
                      ) where k >= k_avg
                    ) where k >= k_avg
                  ) where k >= k_avg
                ) where k >= k_avg
              ) where k >= k_avg
            ) where k >= k_avg
          ) where k >= k_avg
        ) where k >= k_avg
      ) where k >= k_avg
    ) where k >= k_avg
  ) where k >= k_avg
) where k >= k_avg
)

Pelo menos funciona para mim no nível de aninhamento de 30 e produz um plano de execução totalmente diferente com WINDOW BUFFER e VIEW em vez de LOAD TABLE AS SELECT , SORT AGGREGATE e TABLE ACCESS FULL .

Atualizar

  1. Acabei de instalar o 11.2.0.4 (Win7/32bit) e testá-lo em relação à consulta inicial. Nada mudou no comportamento do otimizador.

  2. Não há possibilidade de afetar diretamente o comportamento de um CBO, mesmo com o uso de inline (não documentado) ou RULE (obsoleto) dicas. Pode ser que algum Guru conheça alguma variante, mas é um Top Secret para mim (e para o Google também :-) .

  3. Fazer as coisas em uma instrução de seleção em tempo razoável é possível se uma instrução de seleção principal for separada em partes e colocada na função que retorna um conjunto de linhas (função que retorna sys_refcursor ou cursor de tipo forte), mas não é uma escolha se uma consulta construído em tempo de execução.

  4. A solução alternativa com o uso de XML é possível, mas essa variante parece remover uma amígdala pelo cu (desculpe):

.
select
  extractvalue(column_value,'/t/somevalue') abc
from 
  table(xmlsequence((
    select t2 from (
      select
        t0,
        t1,
        (   
          select xmlagg(
                   xmlelement("t", 
                     xmlelement("k1",extractvalue(t1t.column_value,'/t/k1')), 
                     xmlelement("somevalue", systimestamp))
                  )
          from 
            table(xmlsequence(t0)) t0t, 
            table(xmlsequence(t1)) t1t  
          where 
            extractvalue(t1t.column_value,'/t/k1') >= (
              select avg(extractvalue(t1t.column_value, '/t/k1')) from table(xmlsequence(t1))
            )                                              
            and 
            extractvalue(t0t.column_value,'/t/k2') > 6
        ) t2
      from (
        select
          t0,
          (
            select xmlagg(
                     xmlelement("t", 
                       xmlelement("k1",extractvalue(column_value,'/t/k1')), 
                       xmlelement("somevalue", sysdate))
                    )
            from table(xmlsequence(t0))   
            where 
              extractvalue(column_value,'/t/k1') >= (
                select avg(extractvalue(column_value, '/t/k1')) from table(xmlsequence(t0))
              )
          ) t1
        from (
          select
            xmlagg(xmlelement("t", xmlelement("k1", level), xmlelement("k2", level + 3))) t0
          from dual connect by level < 5
        )
      )
    )
  )))

Outra coisa sobre um código estranho acima é que esta variante é aplicável apenas se with conjuntos de dados não tinham um grande número de linhas.