Je gère une requête dans de grandes tables, et bien que cela fonctionne bien, même difficile, c'est de nombreuses données, j'aimerais comprendre quelle partie de celle-ci pèse sur l'exécution. Malheureusement, je ne suis pas trop bon avec Expliquer les plans, alors j'appelle de l'aide.
Voici quelques données sur ces tables:
history_state_table
7.424.65 lignes (dont seule 13.412 reste après t1.alarm_type = 'AT1'
)costumer_price_history
448.284.169 rangéescycle_table
215 rangéesCe serait la requête (ne vous dérange pas que la logique est juste pour la référence):
SELECT t1.id_alarm, t2.load_id, t2.reference_date
FROM history_state_table t1,
(SELECT op_code, contract_num,
COUNT (DISTINCT id_ponto) AS num_pontos,
COUNT
(DISTINCT CASE
WHEN vlr > 0
THEN id_ponto
ELSE NULL
END
) AS bigger_than_zero,
MAX (load_id) AS load_id,
MAX (reference_date) AS reference_date
FROM costumer_price_history
WHERE load_id IN
(42232, 42234, 42236, 42238, 42240, 42242, 42244) /* arbitrary IDs depending on execution*/
AND sistema = 'F1' /* Hardcoded filters */
AND rec_type = 'F3' /* Hardcoded filters */
AND description = 'F3' /* Hardcoded filters */
AND extract_type IN
('T1', 'T2', 'T3')
GROUP BY op_code, contract_num) t2
WHERE t1.op_code = t2.op_code
AND t1.contract_num = t2.contract_num
AND t1.alarm_type = 'AT1'
AND t1.alarm_status = 'DONE'
AND ( ( t1.prod_type = 'COMBO'
AND t2.bigger_than_zero = t2.num_pontos - 1
)
OR ( t1.prod_type != 'COMBO'
AND t2.bigger_than_zero = t2.num_pontos
)
)
/* arbitrary filter depending on execution*/
AND t1.data_tratado BETWEEN (SELECT data_inicio
FROM cycle_table
WHERE id_ciclo = 160) AND (SELECT data_fim
FROM cycle_table
WHERE id_ciclo =
160)
Et enfin le plan d'explication:
Plan
SELECT STATEMENT ALL_ROWSCost: 5,485
13 NESTED LOOPS
7 NESTED LOOPS Cost: 5,483 Bytes: 115 Cardinality: 1
5 VIEW Cost: 12 Bytes: 59 Cardinality: 1
4 SORT GROUP BY Cost: 12 Bytes: 85 Cardinality: 1
3 INLIST ITERATOR
2 TABLE ACCESS BY INDEX ROWID TABLE RAIDPIDAT.COSTUMER_PRICE_HISTORY Cost: 11 Bytes: 85 Cardinality: 1
1 INDEX RANGE SCAN INDEX RAIDPIDAT.IDX_COSTUMER_PRICE_HISTORY_2 Cost: 10 Cardinality: 3
6 INDEX RANGE SCAN INDEX RAIDPIDAT.IDX_HISTORY_STATE_TABLE_1TPALM Cost: 662 Cardinality: 102,068
12 TABLE ACCESS BY INDEX ROWID TABLE RAIDPIDAT.HISTORY_STATE_TABLE Cost: 5,471 Bytes: 56 Cardinality: 1
9 TABLE ACCESS BY INDEX ROWID TABLE RAIDPIDAT.CYCLE_TABLE Cost: 1 Bytes: 12 Cardinality: 1
8 INDEX UNIQUE SCAN INDEX (UNIQUE) RAIDPIDAT.PK_CYCLE_TABLE Cost: 0 Cardinality: 1
11 TABLE ACCESS BY INDEX ROWID TABLE RAIDPIDAT.CYCLE_TABLE Cost: 1 Bytes: 12 Cardinality: 1
10 INDEX UNIQUE SCAN INDEX (UNIQUE) RAIDPIDAT.PK_CYCLE_TABLE Cost: 0 Cardinality: 1
L'esprit que je ne demande pas "comment le réécrire plus efficacement", mais comment puis-je trouver avec le plan d'explication de l'opération la plus coûteuse. Pendant ce temps, je lis à ce sujet, mais j'apprécierais de l'aide.
Expliquer Plan ne vous dit pas ce qui est en réalité l'opération la plus coûteuse. La colonne "Coût" est un Guess - il s'agit d'une valeur estimée par optimiseur. La colonne "Cardinalité" et la colonne "octets". http://docs.oracle.com/cd/b28359_01/server.111/b28274/ex_plan.htm#i183
Dans votre exemple, votre optimiseur vous dit: Je décide d'utiliser ce plan car Je suppose que la boucle coûterait environ 5 483. Et j'espère que ce serait la partie la plus coûteuse de l'exécution, mais je ne peux pas garantir cela.
La même chose s'applique récursivement à toutes les profondeurs de l'arbre.
Si vous allez en profondeur aux niveaux les plus bas (c'est-à-dire par intuition la plupart des niveaux en boucle, les niveaux la plupart exécutés), vous voyez que l'opération qui s'en tient particulièrement en termes de coût attendu et de nombre attendu d'éléments, est la
6 INDEX RANGE SCAN INDEX RAIDPIDAT.IDX_HISTORY_STATE_TABLE_1TPALM Cost: 662 Cardinality: 102,068
Ainsi, Optimiseur devinait que l'exécution optimale de cette requête est de boucler beaucoup autour d'un mauvais cheval de travail raidpidat.idx_history_state_table_1tpalm. Je ne peux vraiment pas voir quelle partie de votre requête le concerne directement, mais je soupçonne que t1.data_tratado condition. Et encore une fois, je ne peux pas voir si elle est vraiment la partie la plus coûteuse.
Je vais essayer de traduire la syntaxe des boucles dans le plan d'explication au pseudo-code procédural:
/* begin step 13 (by "step 13" I mean a line that reads " 13 NESTED LOOPS") */
/* begin step 7 */
do step 5
myresult = rows from step 5
for each row from myresult {
do step 6
for each row from step 6 {
join to a row from myresult the matching row from step 6
}
}
/* end step 7 */
for each row from myresult {
do step 12
for each row from step 12 {
join to a row from myresult the matching row from step 12
}
}
/* end step 13 */
return myresult
On dirait compliqué, mais un but vraiment de chaque "boucle imbriquée" est de créer une jointure (une seule table faite de deux tables) de la manière la plus naïve, une boucle-inside-a-boucle.
Le plan d'explication n'est qu'une prédiction des méthodes de jointure qui seront utilisées lors de l'exécution d'une requête. Cela peut empêcher de déduire quelle étape prend le plus de temps, car un plan différent peut être suivi lorsque la déclaration est exécutée.
Pour obtenir des statistiques réelles sur la durée de la durée de chaque étape, vous devez exécuter une SQL TRACE de l'instruction et consultez le fichier de trace - manuellement ou à l'aide d'un outil tel que TKProf . Cela vous montrera combien de lignes chaque étape traitée et combien de temps il a fallu.
Cela dit, en regardant le Cardinality
inscrit à la fin de chaque ligne donnera une indication de combien de lignes doivent être traitées. Étapes Traitement Plusieurs rangées sont susceptibles de prendre plus de temps à exécuter car il y a plus de travail à faire.
Donc dans votre exemple ligne 6 INDEX RANGE SCAN INDEX RAIDPIDAT.IDX_HISTORY_STATE_TABLE_1TPALM Cost: 662 Cardinality: 102,068
qui devrait traiter 102 068 lignes est susceptible d'être la plus chère que les autres étapes prédisent une rangée. Ceci n'est vrai que si ces estimations de cardinalité sont toutefois précises; Vous devrez vérifier que ces cardinalités correspondent aux lignes réelles retournées. Le moyen le plus simple de le faire est via une trace SQL comme indiqué ci-dessus.