Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Niska wydajność dla głęboko zagnieżdżonego faktoringu podzapytań (CTE)

P1:Wydaje się, że nie ma nic o czasie obliczania, tylko błąd w algorytmie optymalizacyjnym, który sprawia, że ​​jest szalony podczas obliczania najlepszego planu wykonania.

P2:Istnieje wiele znanych i naprawionych błędów w Oracle 11.X.0.X związanych z optymalizacją zapytań zagnieżdżonych i faktoringiem zapytań. Ale bardzo trudno jest znaleźć konkretny problem.

P3:Istnieją dwa nieudokumentowane podpowiedzi:materialize i inline ale żaden z nich nie działa dla mnie, gdy próbowałem twojego przykładu. Możliwe, że niektóre zmiany w konfiguracji serwera lub aktualizacja do wersji 11.2.0.3 mogą zwiększyć limit zagnieżdżonych with klauzule:dla mnie (na 11.2.0.3 Win7/x86) twój przykład działa dobrze, ale zwiększenie liczby zagnieżdżonych tabel do 30 zawiesza sesję.

Obejście może wyglądać tak:

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
)

Przynajmniej u mnie działa na poziomie zagnieżdżenia 30 i tworzy zupełnie inny plan wykonania z WINDOW BUFFER i VIEW zamiast LOAD TABLE AS SELECT , SORT AGGREGATE i TABLE ACCESS FULL .

Aktualizacja

  1. Właśnie zainstalowałem 11.2.0.4 (Win7/32bit) i przetestowałem go z początkowym zapytaniem. Nic się nie zmieniło w zachowaniu optymalizatora.

  2. Nie ma możliwości bezpośredniego wpływu na zachowanie CBO, nawet przy użyciu inline (nieudokumentowane) lub RULE (przestarzałe) podpowiedzi. Może jakiś guru zna jakiś wariant, ale dla mnie (i Google też :-) jest to ściśle tajne .

  3. Wykonywanie czynności za pomocą jednej instrukcji select w rozsądnym czasie jest możliwe, jeśli główna instrukcja select podzielona na części i umieszczona w funkcji, która zwraca zestaw wierszy (funkcja zwracająca sys_refcursor lub kursor o silnym typie), ale nie jest to wybór, jeśli zapytanie zbudowany w czasie wykonywania.

  4. Obejście z użyciem XML jest możliwe, ale ten wariant wygląda jak usunięcie migdałka przez dziurę w dupie (przepraszam):

.

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

Inną rzeczą związaną z dziwnym kodem powyżej jest to, że ten wariant ma zastosowanie tylko wtedy, gdy with zestawy danych nie miały dużej liczby wierszy.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zamienić pierwsze trzy znaki ciągu w Oracle?

  2. Oracle SQL:wybierz z tabeli z zagnieżdżoną tabelą

  3. Jak przekonwertować starszą instrukcję lewego sprzężenia zewnętrznego w Oracle?

  4. jak wyzwolić aktualizację wartości wiersza tabeli przy użyciu wartości wiersza z innej tabeli w Apex Oracle SQL?

  5. Wyrażenie regularne określonego formatu daty