sql >> Databasteknik >  >> RDS >> Oracle

Långsam prestanda för djupt kapslad subquery factoring (CTE)

F1:Det verkar som att det inte finns något om beräkningstid, bara fel i optimeringsalgoritmen som gör det galet när man beräknar en plan för bästa utförande.

F2:Det finns ett antal kända och fixade buggar i Oracle 11.X.0.X relaterade till optimering av kapslade frågor och query factoring. Men det är väldigt svårt att hitta en konkret fråga.

F3:Det finns två odokumenterade tips:materialize och inline men ingen av dem fungerar för mig medan jag provade ditt exempel. Det är möjligt att vissa ändringar i serverkonfigurationen eller uppgradering till 11.2.0.3 kan öka gränsen för kapslade with klausuler:för mig (på 11.2.0.3 Win7/x86) fungerar ditt exempel bra, men ökat antal kapslade tabeller till 30 hänger en session.

Lösningen kan se ut så här:

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
)

Åtminstone fungerar det för mig på kapslingsnivå på 30 och ger en helt annan exekveringsplan med WINDOW BUFFER och VIEW istället för LOAD TABLE AS SELECT , SORT AGGREGATE och TABLE ACCESS FULL .

Uppdatera

  1. Installerade precis 11.2.0.4 (Win7/32bit) och testade den mot den första frågan. Ingenting har förändrats i optimerarens beteende.

  2. Det finns ingen möjlighet att direkt påverka ett CBO-beteende, även med användning av inline (odokumenterad) eller RULE (föråldrade) tips. Det kanske är någon guru som känner till en variant, men det är en topphemlighet för mig (och Google också :-) .

  3. Att göra saker i en one select-sats inom rimlig tid är möjligt om en huvudselect-sats separeras i delar och placeras i funktionen som returnerar en uppsättning rader (funktion som returnerar sys_refcursor eller starkt skriven markör), men det är inte ett val om en fråga konstruerad vid körning.

  4. Lösning med användning av XML är möjlig, men den här varianten ser ut som att ta bort en tonsill genom rövhålet (förlåt):

.

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

En annan sak med en konstig kod ovan är att denna variant endast gäller om with datauppsättningar hade inte ett stort antal rader.



  1. Hur man gör subtraktion mellan två fält i en tabell med hjälp av en SQL-fråga

  2. JDBC - Statement, PreparedStatement, CallableStatement och cachning

  3. ORA-00913 Fel vid användning av mycket IN-sats

  4. Accenttecken har inte importerats korrekt med BULK INSERT