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