Oracle
 sql >> Database >  >> RDS >> Oracle

Prestazioni lente per il factoring di sottoquery profondamente annidato (CTE)

Q1:Sembra che non ci sia nulla sul tempo di calcolo, solo bug nell'algoritmo di ottimizzazione che lo rendono pazzo durante il calcolo di un piano di esecuzione migliore.

D2:Esistono numerosi bug noti e corretti in Oracle 11.X.0.X relativi all'ottimizzazione delle query nidificate e al fattore di query. Ma è molto difficile trovare un problema concreto.

Q3:Ci sono due non documentati suggerimenti:materialize e inline ma nessuno di loro funziona per me mentre ho provato il tuo esempio. È possibile che alcune modifiche alla configurazione del server o l'aggiornamento a 11.2.0.3 possano aumentare il limite di with nidificato clausole:per me (su 11.2.0.3 Win7/x86) il tuo esempio funziona bene, ma aumentando il numero di tabelle nidificate a 30 si blocca una sessione.

La soluzione potrebbe essere simile a questa:

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
)

Almeno funziona per me a livello di annidamento 30 e produce un piano di esecuzione completamente diverso con WINDOW BUFFER e VIEW invece di LOAD TABLE AS SELECT , SORT AGGREGATE e TABLE ACCESS FULL .

Aggiorna

  1. Ho appena installato 11.2.0.4 (Win7/32bit) e testalo rispetto alla query iniziale. Nulla è cambiato nel comportamento dell'ottimizzatore.

  2. Non ci sono possibilità di influenzare direttamente un comportamento CBO, anche con l'uso di inline (non documentato) o RULE (obsoleto) suggerimenti. Forse qualche Guru conosce una qualche variante, ma è un Top Secret per me (e anche per Google :-) .

  3. È possibile eseguire operazioni in un'istruzione one select in un tempo ragionevole se un'istruzione select principale è separata in parti e inserita nella funzione che restituisce un insieme di righe (funzione che restituisce sys_refcursor o un cursore digitato forte), ma non è una scelta se una query costruito in fase di esecuzione.

  4. È possibile una soluzione alternativa con l'utilizzo di XML, ma questa variante sembra rimuovere una tonsilla attraverso il buco del culo (scusate):

.

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

Un'altra cosa su uno strano codice sopra è che questa variante è applicabile solo se with i set di dati non avevano un numero elevato di righe.