Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Langsame Leistung für tief verschachteltes Subquery Factoring (CTE)

Q1:Scheint, dass es nichts über die Berechnungszeit gibt, nur Fehler im Optimierungsalgorithmus, die ihn bei der Berechnung eines Best-Execution-Plans verrückt machen.

F2:Es gibt eine Reihe bekannter und behobener Fehler in Oracle 11.X.0.X im Zusammenhang mit der Optimierung von verschachtelten Abfragen und der Faktorisierung von Abfragen. Aber es ist sehr schwer, ein konkretes Problem zu finden.

F3:Es gibt zwei undokumentierte Hinweise:materialize und inline aber keiner von ihnen funktioniert für mich, während ich Ihr Beispiel ausprobiert habe. Es ist möglich, dass einige Änderungen in der Serverkonfiguration oder ein Upgrade auf 11.2.0.3 das Limit für verschachtelte with erhöhen Klauseln:Für mich (auf 11.2.0.3 Win7/x86) funktioniert Ihr Beispiel gut, aber die Erhöhung der Anzahl verschachtelter Tabellen auf 30 hängt eine Sitzung auf.

Abhilfe könnte wie folgt aussehen:

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
)

Zumindest funktioniert es für mich auf Verschachtelungsebene von 30 und erzeugt einen völlig anderen Ausführungsplan mit WINDOW BUFFER und VIEW statt LOAD TABLE AS SELECT , SORT AGGREGATE und TABLE ACCESS FULL .

Aktualisieren

  1. Einfach 11.2.0.4 (Win7/32bit) installiert und mit der ersten Abfrage getestet. Am Optimiererverhalten hat sich nichts geändert.

  2. Es gibt keine Möglichkeit, ein CBO-Verhalten direkt zu beeinflussen, auch nicht bei Verwendung von inline (undokumentiert) oder RULE (veraltete) Hinweise. Vielleicht kennt irgendein Guru eine Variante, aber für mich (und Google auch :-) .

    ist es streng geheim
  3. Es ist möglich, Dinge in einer One-Select-Anweisung in angemessener Zeit zu erledigen, wenn eine Haupt-Select-Anweisung in Teile getrennt und in die Funktion eingefügt wird, die eine Reihe von Zeilen zurückgibt (Funktion, die sys_refcursor oder einen stark typisierten Cursor zurückgibt), aber es ist keine Wahl, wenn eine Abfrage zur Laufzeit erstellt.

  4. Problemumgehung mit Verwendung von XML ist möglich, aber diese Variante sieht aus wie das Entfernen einer Mandel durch das Arschloch (Entschuldigung):

.

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

Eine andere Sache über einen seltsamen Code oben ist, dass diese Variante nur anwendbar ist, wenn with Datensätze hatten keine große Anzahl von Zeilen.