sql >> Base de Datos >  >> RDS >> Oracle

Rendimiento lento para la factorización de subconsultas profundamente anidadas (CTE)

P1:Parece que no hay nada sobre el tiempo de cálculo, solo un error en el algoritmo del optimizador que lo vuelve loco al calcular el mejor plan de ejecución.

P2:Hay una serie de errores conocidos y corregidos en Oracle 11.X.0.X relacionados con la optimización de consultas anidadas y la factorización de consultas. Pero es muy difícil encontrar un problema concreto.

P3:Hay dos indocumentados consejos:materialize y inline pero ninguno de ellos funciona para mí mientras probé tu ejemplo. Es posible que algunos cambios en la configuración del servidor o la actualización a 11.2.0.3 puedan aumentar el límite de with anidados cláusulas:para mí (en 11.2.0.3 Win7/x86) su ejemplo funciona bien, pero aumentar el número de tablas anidadas a 30 bloquea una sesión.

La solución puede verse así:

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
)

Al menos funciona para mí en el nivel de anidamiento de 30 y produce un plan de ejecución totalmente diferente con WINDOW BUFFER y VIEW en lugar de LOAD TABLE AS SELECT , SORT AGGREGATE y TABLE ACCESS FULL .

Actualizar

  1. Acabo de instalar 11.2.0.4 (Win7/32bit) y pruébelo con la consulta inicial. Nada cambió en el comportamiento del optimizador.

  2. No hay posibilidad de afectar directamente el comportamiento de una CBO, incluso con el uso de inline (sin documentar) o RULE (en desuso) sugerencias. Puede haber algún Gurú que conozca alguna variante, pero es un alto secreto para mí (y para Google también :-).

  3. Es posible hacer cosas en una declaración de selección única en un tiempo razonable si una declaración de selección principal se separa en partes y se coloca en la función que devuelve un conjunto de filas (función que devuelve sys_refcursor o cursor de tipo fuerte), pero no es una opción si una consulta construido en tiempo de ejecución.

  4. Es posible una solución con el uso de XML, pero esta variante parece quitar una amígdala por el agujero del culo (perdón):

.

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
        )
      )
    )
  )))

Otra cosa sobre un código extraño anterior es que esta variante solo se aplica si with los conjuntos de datos no tenían una gran cantidad de filas.