Oracle
 sql >> Base de données >  >> RDS >> Oracle

Performances lentes pour la factorisation de sous-requêtes profondément imbriquées (CTE)

Q1 :Il semble qu'il n'y ait rien sur le temps de calcul, juste un bogue dans l'algorithme de l'optimiseur qui le rend fou lors du calcul d'un meilleur plan d'exécution.

Q2 :Il existe un certain nombre de bogues connus et corrigés dans Oracle 11.X.0.X liés à l'optimisation des requêtes imbriquées et à la factorisation des requêtes. Mais il est très difficile de trouver un problème concret.

Q3 :Il y a deux sans papiers indices :materialize et inline mais aucun d'entre eux ne travaille pour moi pendant que j'essayais votre exemple. Il est possible que certaines modifications de la configuration du serveur ou la mise à niveau vers 11.2.0.3 augmentent la limite de with imbriqué clauses :pour moi (sur 11.2.0.3 Win7/x86), votre exemple fonctionne bien, mais l'augmentation du nombre de tables imbriquées à 30 bloque une session.

La solution de contournement peut ressembler à ceci :

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
)

Au moins, cela fonctionne pour moi au niveau d'imbrication de 30 et produit un plan d'exécution totalement différent avec WINDOW BUFFER et VIEW au lieu de LOAD TABLE AS SELECT , SORT AGGREGATE et TABLE ACCESS FULL .

Mettre à jour

  1. Juste installé 11.2.0.4 (Win7/32bit) et testez-le par rapport à la requête initiale. Rien n'a changé dans le comportement de l'optimiseur.

  2. Il n'y a aucune possibilité d'affecter directement un comportement CBO, même avec l'utilisation de inline (non documenté) ou RULE (obsolète) conseils. Peut-être qu'un gourou connaît une variante, mais c'est un Top Secret pour moi (et Google aussi :-) .

  3. Faire les choses dans une instruction de sélection unique dans un délai raisonnable est possible si une instruction de sélection principale séparée en plusieurs parties et placée dans la fonction qui renvoie un ensemble de lignes (fonction renvoyant sys_refcursor ou curseur de type fort), mais ce n'est pas un choix si une requête construit au moment de l'exécution.

  4. Une solution de contournement avec l'utilisation de XML est possible, mais cette variante ressemble à retirer une amygdale par le trou du cul (désolé):

.

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

Une autre chose à propos d'un code étrange ci-dessus est que cette variante s'applique uniquement si with les ensembles de données n'avaient pas un grand nombre de lignes.