Question

La situation

J'ai des problèmes avec mon plan d'exécution de la requête pour une requête de taille moyenne sur une grande quantité de données dans Oracle 11.2.0.2.0. Pour accélérer les choses, j'introduit un filtre de gamme qui fait à peu près quelque chose comme ceci:

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((org_from IS NULL) OR (org_from <= org.no))
   AND ((org_to   IS NULL) OR (org_to   >= org.no)))
  -- [...]

Comme vous pouvez le voir, je veux limiter la JOIN de organisations en utilisant une gamme facultative de numéros d'organisation. Le code client peut appeler DO_STUFF avec (censé être rapide) ou sans (très lent) la restriction.

The Trouble

Le problème est, PL / SQL créer des variables bind pour les paramètres de org_from et org_to ci-dessus, ce qui est ce que j'attendre dans la plupart des cas:

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((:B1 IS NULL) OR (:B1 <= org.no))
   AND ((:B2 IS NULL) OR (:B2 >= org.no)))
  -- [...]

La solution

Dans ce cas seulement, je mesure le plan d'exécution de requête pour être beaucoup mieux quand je juste inline les valeurs, à savoir quand est en fait quelque chose comme la requête exécutée par Oracle

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))
  -- [...]

Par « beaucoup », je veux dire 5-10x plus vite. Notez que la requête est exécutée très rarement, à savoir une fois par mois. Donc, je ne ai pas besoin de mettre en cache le plan d'exécution.

Mes questions

  • Comment puis-je inline valeurs dans PL / SQL? Je sais que sur le EXECUTE IMMEDIATE, mais je préférerais avoir PL / SQL compilation ma requête, et non concaténer string.

  • ai-je mesurer quelque chose qui est arrivé par hasard ou puis-je suppose que les variables inline est en effet mieux (dans ce cas)? La raison pour laquelle je demande est parce que je pense que les variables de liaison forcer Oracle à concevoir un général plan d'exécution, tandis que les valeurs inline permettraient d'analyser colonne très spécifiques et des statistiques index . Je peux donc imaginer que ce n'est pas seulement une coïncidence.

  • Am quelque chose que je manque? Peut-être il y a un tout autre moyen de parvenir à la requête amélioration du plan d'exécution, autre que inline variable (note que j'ai essayé pas mal de conseils aussi bien, mais je ne suis pas un expert en ce domaine)?

Était-ce utile?

La solution

Dans un de vos commentaires, vous avez dit:

"J'ai également vérifié les différentes valeurs de liaison. Avec des variables bind je reçois une INTÉGRAL TABLEAU BALAYAGES, alors qu'avec codée en dur les valeurs, le plan ressemble beaucoup mieux. "

Il y a deux chemins. Si vous passez NULL pour les paramètres, vous sélectionnez tous les enregistrements. Dans ces circonstances, un balayage Tableau complet est la façon la plus efficace de la récupération de données. Si vous passez des valeurs puis lit indexé peut être plus efficace, parce que vous êtes seulement la sélection d'un petit sous-ensemble de l'information.

Lorsque vous formulez la requête en utilisant des variables liées à l'optimiseur doit prendre une décision: faut-il présumer que la plupart du temps, vous passerez des valeurs ou que vous passez nulls? Difficile. Alors regardez une autre façon: est-il plus inefficace de faire une analyse complète de la table lorsque vous ne devez sélectionner un sous-ensemble d'enregistrements, ou n'indexé lorsque vous avez besoin se lit pour sélectionner tous les enregistrements?

Il semble que l'optimiseur a pour regonflée analyses complètes de table comme étant le moins un fonctionnement inefficace pour couvrir toutes les éventualités.

Alors que lorsque vous coder en dur les valeurs de l'Optimiseur sait immédiatement qui permet d'évaluer de 10 IS NULL à FAUX, et il peut peser les avantages de l'utilisation indexé se lit comme suit pour trouver les enregistrements sous-ensemble souhaités.


Alors, quoi faire? Comme vous le dites cette requête est exécutée seulement une fois par mois, je pense que ce ne nécessiterait qu'une légère modification des processus d'affaires pour avoir des requêtes distinctes: une pour toutes les organisations et un pour un sous-ensemble d'organisations

.

"BTW, la suppression: R1 clause IS NULL ne change pas le plan d'exécution beaucoup, ce qui me laisse avec l'autre côté de la condition OU, R1 <= org.no où NULL ne serait pas logique De toute façon, comme org.no est NOT NULL "

D'accord, donc la chose est que vous avez une paire de variables de liaison qui spécifient une gamme . En fonction de la répartition des valeurs, différentes gammes pourraient convenir à différents plans d'exécution. C'est, cette gamme serait (probablement) convenir à une plage indexée de balayage ...

WHERE org.id BETWEEN 10 AND 11

... alors que cela est susceptible d'être plus adapté à une analyse table complète ...

WHERE org.id BETWEEN 10 AND 1199999

C'est là Bind variable Peeking entre en jeu.

(selon la répartition des valeurs, bien sûr).

Autres conseils

Étant donné que les plans de requête sont en fait toujours différents, qui implique que les estimations de cardinalité de l'optimiseur sont éteints pour une raison quelconque. Pouvez-vous confirmer des plans de requête que l'optimiseur attend les conditions insuffisamment sélectif lorsque variables de liaison sont utilisées? Puisque vous utilisez 11.2, Oracle devrait utiliser le partage du curseur adaptatif il ne doit pas être une variable de liaison question jeter un oeil (en supposant que vous appelez la version avec des variables se lient plusieurs fois avec différentes valeurs de NO dans vos tests.

Les estimations de cardinalité sur le bon plan en fait correct? Je sais que vous avez dit que les statistiques sur la colonne de NO sont exacts, mais je serais suspecte d'un histogramme parasite qui ne peut pas être mis à jour par vos statistiques régulières de collecte processus, par exemple.

Vous pouvez toujours utiliser une touche dans la requête pour forcer un index particulier à utiliser (si en utilisant un stockées contour ou la stabilité du plan d'optimiseur serait préférable du point de vue de l'entretien à long terme). Une de ces options serait préférable de recourir à SQL dynamique.

Un test supplémentaire pour essayer, cependant, serait de remplacer le SQL 99 syntaxe de jointure avec l'ancienne syntaxe d'Oracle, i.e..

SELECT <<something>>
  FROM <<some other table>> cust,
       organization org
 WHERE cust.org_id = org.id
   AND (    ((org_from IS NULL) OR (org_from <= org.no)) 
        AND ((org_to   IS NULL) OR (org_to   >= org.no)))

Ce changement ne devrait évidemment pas quoi que ce soit, mais il y a eu des problèmes d'analyseur avec la syntaxe SQL 99 de sorte que quelque chose à vérifier.

It smells like Bind Peeking, but I am only on Oracle 10, so I can't claim the same issue exists in 11.

This looks a lot like a need for Adaptive Cursor Sharing, combined with SQLPlan stability. I think what is happening is that the capture_sql_plan_baselines parameter is true. And the same for use_sql_plan_baselines. If this is true, the following is happening:

  1. The first time that a query started it is parsed, it gets a new plan.
  2. The second time, this plan is stored in the sql_plan_baselines as an accepted plan.
  3. All following runs of this query use this plan, regardless of what the bind variables are.

If Adaptive Cursor Sharing is already active,the optimizer will generate a new/better plan, store it in the sql_plan_baselines but is not able to use it, until someone accepts this newer plan as an acceptable alternative plan. Check dba_sql_plan_baselines and see if your query has entries with accepted = 'NO' and verified = null You can use dbms_spm.evolve to evolve the new plan and have it automatically accepted if the performance of the plan is at least 1,5 times better than without the new plan.

I hope this helps.

I added this as a comment, but will offer up here as well. Hope this isn't overly simplistic, and looking at the detailed responses I may be misunderstanding the exact problem, but anyway...

Seems your organisations table has column no (org.no) that is defined as a number. In your hardcoded example, you use numbers to do the compares.

JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))

In your procedure, you are passing in varchar2:

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

So to compare varchar2 to number, Oracle will have to do the conversions, so this may cause the full scans.

Solution: change proc to pass in numbers

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top