2009-04-07 5 views
3

J'ai une requête paramétrée. Selon les valeurs des paramètres, le plan de requête optimal varie de manière significative. Voici le problème: Oracle utilise le plan à partir de l'invocation de la première requête pour les invocations ultérieures entraînant de mauvaises performances. Je m'occupe de ça par SQL dynamique mais cette façon est loin d'être élégante. La question est donc la suivante: existe-t-il un moyen de dire à Oracle que le plan de requête doit être recalculé?Existe-t-il un moyen de demander à Oracle de recalculer un plan de requête pour chaque appel de requête?

Répondre

2

Pour Oracle 10g nous choisirions une table dans la requête et exécuter

GRANT SELECT ON table1 TO user1; 

perdriez le plan d'une requête faisant référence à cette table. Bien sûr, vous voudriez choisir une table qui a un impact minimal sur les autres requêtes. Voir aussi this page pour plus d'informations et un exemple de liste.

4

Si le plan de requête change vraiment de manière significative sur la valeur du paramètre, vous ne devriez peut-être pas utiliser de variables de liaison pour ce paramètre.

Combien de valeurs différentes ce paramètre peut-il prendre? S'il n'y en a que quelques-uns, vous obtiendrez un ou deux plans de requête (un pour chaque valeur), et ceux-ci, espérons-le, fonctionneront bien et pourront être réutilisés. Vous pouvez également utiliser les commentaires "/ * C'EST LA VALEUR BRACKET UN * /" dans l'instruction SQL pour les séparer (ou interroger les indicateurs de l'analyseur, si vous pensez que vous savez lesquels sont appropriés, quelque chose comme/* + CARDINALITY */pourrait s'appliquer ici).

De toute façon, je pense que vous voulez avoir des instructions SQL séparées afin que vous puissiez obtenir des rapports séparés dans Statspack et vos amis, car il semble que vous vouliez affiner cette requête.

2

Si vous voulez vraiment générer un nouveau plan d'interrogation à chaque fois, vient de mettre un commentaire unique comme le suggère Thilo

select /* SQLID=1234 */ 1 from dual; 
select /* SQLID=1235 */ 1 from dual; 

Ceux-ci devraient générer des plans uniques. Je voudrais être très méfiant de la nécessité de faire cela, cependant, avant d'essayer de contourner l'optimiseur, vous devriez être très sûr que vos statistiques ne sont pas fausses.

+0

Je ne peux pas modifier la source SQL. Les statistiques sont bonnes. Dans mon cas, le problème est que le plan de requête est obsolète et mauvais. Si j'invalide le plan, Oracle calculera un nouveau bon plan. –

+0

Il-Bhima est le chemin à parcourir alors si vous ne pouvez pas changer la source SQL du tout. –

2

L'optimiseur utilise notamment des histogrammes sur les colonnes associées. Si vous utilisez une variable de liaison et si vous avez des histogrammes dans la colonne correspondante, le plan peut changer en fonction de la valeur du paramètre. Ce premier plan restera dans le pool partagé et sera utilisé pour toutes les valeurs.

Si vous ne le souhaitez pas, vous pouvez utiliser des littéraux au lieu de binds (si vous n'avez pas trop de versions du même sql). Ou vous pouvez supprimer l'histogramme, en supprimant l'histogramme assure que indépendant de la valeur du paramètre de liaison le même plan sera généré. Invalider le sql pour chaque exécution n'est pas une bonne idée. En fonction de la fréquence d'utilisation de ce sql, de nouveaux problèmes peuvent survenir, tels que les problèmes de verrouillage causés par l'analyse syntaxique.

2

Existe-t-il un moyen de dire à Oracle que le plan de requête doit être recalculé?

Vous pouvez créer plusieurs OUTLINE « s pour les différents plans d'exécution et sélectionner celui à utiliser à l'aide OUTLINE CATEGORIES:

CREATE OUTLINE ol_use_nl 
FOR 
SELECT * 
FROM mytable1 mt1 
JOIN mytable2 mt2 
ON  mt1.id = mt2.id 
WHERE mt1.value BETWEEN :a AND :b 
CATEGORY FILTERED; 

/* Edit the outline to add USE_NL */ 

CREATE OUTLINE ol_use_nl 
FOR 
SELECT * 
FROM mytable1 mt1 
JOIN mytable2 mt2 
ON  mt1.id = mt2.id 
WHERE mt1.value BETWEEN :a AND :b 
CATEGORY UNFILTERED; 

/* Edit the outline to add USE_HASH */ 

ALTER SESSION SET USE_STORED_OUTLINES = FILTERED; 

SELECT * 
FROM mytable1 mt1 
JOIN mytable2 mt2 
ON  mt1.id = mt2.id 
WHERE mt1.value BETWEEN 1 AND 2 

/* This will use NESTED LOOPS */ 

ALTER SESSION SET USE_STORED_OUTLINES = UNFILTERED; 

SELECT * 
FROM mytable1 mt1 
JOIN mytable2 mt2 
ON  mt1.id = mt2.id 
WHERE mt1.value BETWEEN 1 AND 1000000 

/* This will use HASH JOIN */ 
0

Votre problème est dû à lier la variable Peeking - le mettre hors tension pour la base de données entière serait probablement casser d'autres choses, mais vous pouvez la désactiver pour seulement cette requête en ajoutant l'indication suivante:

/* + opt_param (« _ OPTIM_PEEK_USER_BINDS », FAUX) */

+0

Il veut probablement le contraire, "coup d'oeil à chaque fois". Je me demande s'il y a un indice pour cela aussi. – Thilo

+0

"Peek Every Time" est d'utiliser des littéraux. –

0

L'OP nous dit qu'il ne peut pas modifier les instructions sql. Avec l'utilisation du paquet dbms_advanced_rewrite, il est possible d'intercepter une instruction SQL et de modifier cette instruction SQL.

Questions connexes