Question

Que puis-je faire pour améliorer les performances d'une requête Oracle sans créer d'index?

Voici la requête que je tente d'exécuter plus rapidement:

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a,
itempages b,
keygroupdata c
WHERE a.ItemType IN (112,115,189,241)
AND a.ItemNum = b.ItemNum
AND b.ItemNum = c.ItemNum
ORDER BY a.DateStored DESC

Aucune de ces colonnes n'est indexée et chacune des tables contient des millions d'enregistrements. Il va sans dire que l'exécution de la requête prend plus de 3 minutes et demie. Il s'agit d'une base de données tierce dans un environnement de production et je ne suis pas autorisé à créer d'index. Toute amélioration des performances devra donc être apportée à la requête elle-même.

Merci!

Était-ce utile?

La solution

Tout d'abord, je réécrivais la requête pour qu'elle soit conforme à la norme ANSI:

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a
INNER JOIN itempages b ON b.ItemNum = a.ItemNum
INNER JOIN keygroupdata c ON c.ItemNum = b.ItemNum
WHERE a.ItemType IN (112,115,189,241)
ORDER BY a.DateStored DESC

Cela facilite la lecture et la compréhension de ce qui se passe. Cela vous aide également à ne pas commettre d’erreurs (c’est-à-dire de jointures croisées) qui pourraient causer de gros problèmes. Ensuite, je recevrais le plan Explain pour voir ce que le SGBD fait avec cette requête. Est-ce qu'il essaie d'utiliser des index? Rejoignez-vous les tables correctement?

Ensuite, je passerai en revue les tables avec lesquelles je travaille pour voir s’il existe déjà des index que je pourrais utiliser pour accélérer ma requête. Enfin, comme tout le monde l’a suggéré, je supprimerais la clause Order By et le ferais simplement dans le code.

Autres conseils

Demandez au tiers d’indexer ses colonnes de jointure, comme il aurait dû le faire en premier lieu! Sans index, Oracle n'a pas d'autre solution que la force brutale.

Vous pouvez essayer de créer une vue matérialisée sur l’une de ces tables. Vous pouvez ensuite créer un index sur la vue matérialisée qui accélérera la requête (qui interrogerait alors la vue matérialisée au lieu de la table brute).

Bien sûr, si votre table sous-jacente est mise à jour, votre vue et les index devront être actualisés.

Tout d'abord, regardez le plan d'exécution. Reflète-t-il avec précision le nombre de lignes à récupérer à chaque étape de l'exécution de la requête? Quel est le degré de sélectivité du prédicat "a.ItemType IN (112, 115, 189, 241)"? Le plan d’exécution indique-t-il une quelconque utilisation d’espace disque temporaire pour les jointures ou les tris?

En fait, vous pouvez peut-être modifier la question pour inclure le plan d'exécution.

Assurez-vous également que les jointures de hachage ne sont pas désactivées, ce qui est parfois le cas dans les systèmes optimisés pour OLTP, car elles constituent le moyen le plus efficace de placer des données en vrac dans Oracle en bloc. Ils doivent apparaître dans le plan d'exécution.

Vous pouvez essayer de filtrer le type d'élément avant de joindre vos tables, comme indiqué ici.

Si vous utilisez Oracle avant la version 9i, cela présenterait parfois des avantages surprenants.

select 
  c.claimnumber,
  a.itemdate, 
  c.dtn,
  b.filepath
from 
  (
  select itemdate
  from items it
  where it.itemtype in(112,115,189,241)
  ) a
  itempages b,
  keygroupdata c
where a.itemnum = b.itemnum
  and b.itemnum = c.itemnum

Vous pouvez également essayer d’ajouter les astuces / + RULE / ou / + ORDERED / pour voir ce qui se passe ... encore une fois, en particulier avec les anciennes versions. donner des résultats surprenants.

SELECT /*+RULE*/
  c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM
  items a,
  itempages b,
  keygroupdata c
WHERE a.ItemType IN (112,115,189,241)
  AND a.ItemNum = b.ItemNum
  AND b.ItemNum = c.ItemNum
ORDER BY a.DateStored DESC

Si les entrées de la requête sont constantes ou prévisibles ( itemType IN (...) ), vous pouvez également exécuter la requête une ou deux fois par jour et stocker les résultats dans un répertoire local. tableau, avec des indices le cas échéant.

Vous pouvez ensuite rendre la requête coûteuse «hors ligne» et obtenir de meilleurs résultats plus rapidement pour une requête interactive.

S'agit-il d'une requête que vous exécutez souvent? Il semble que le propriétaire de la base de données aurait intérêt à créer les index dont vous avez besoin pour accélérer cette requête. Les 3,5 minutes que vous passez à exécuter la requête doivent avoir un impact sur leur environnement de production!

De plus, ont-ils exécuté des statistiques de mise à jour sur les tables? Cela pourrait améliorer les performances, car l'ordre de jointure est calculé sur la base des statistiques des tables.

BTW, qu'est-ce que vous êtes autorisé à faire? Viens de lire? Si vous pouvez créer des tables temporaires et y mettre des index, je pourrais envisager de faire des copies temporaires de la table, de les indexer, puis de faire la jointure assistée par index avec les copies temporaires.

Je sais que ce fil de discussion est très ancien, mais je voulais tout de même proposer aux moteurs de recherche une solution alternative qui fonctionnerait sur oracle et pourrait être beaucoup plus rapide en fonction des données.

with a as (
  select 
    * 
  from 
    items 
  where 
    ItemType IN (112,115,189,241)
)
SELECT 
  c.ClaimNumber
  , a.ItemDate
  , c.DTN, b.FilePath
FROM 
  a,
  itempages b,
  keygroupdata c
WHERE 
  a.ItemNum = b.ItemNum
  AND b.ItemNum = c.ItemNum
ORDER BY 
  a.DateStored DESC

Vous pouvez également essayer l'indicateur / * + MATERIALIZE * / dans la clause WITH .

En fait, je trouve la syntaxe ancienne de jointure d'Oracle beaucoup plus facile à lire que ansi sql ^^

Sans l'indexation, cette requête ne va qu'empirer à mesure que la taille de la table augmente. Cela dit, essayez de supprimer la clause order by et de procéder de la sorte côté client.

Parfois, vous pouvez voir un avantage en ajoutant des chemins supplémentaires à choisir pour l'optimiseur en ajoutant ce qui semble être des éléments redondants à la clause where.

Par exemple, vous avez A.ItemNum = B.ItemNum ET B.ItemNum = C.ItemNum. Essayez d’ajouter A.ItemNum = C.ItemNum également. Cependant, je suis à peu près sûr que l'optimiseur est suffisamment intelligent pour le résoudre lui-même - cela vaut la peine d'essayer.

En fonction du type de données de la colonne ItemType, l'exécution plus rapide peut être accélérée à l'aide des éléments suivants: s'il s'agit d'un varchar, Oracle impliquera les conversions.

SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath
FROM items a,
itempages b,
keygroupdata c
WHERE ((a.ItemType IN ('112','115','189','241'))
AND (a.ItemNum = b.ItemNum)
AND (b.ItemNum = c.ItemNum))
ORDER BY a.DateStored DESC

Les statistiques sont-elles rassemblées sur ces tables? Sinon, la collecte de statistiques pourrait modifier le plan d'exécution, même si ce ne serait pas nécessairement pour le mieux.

En dehors de cela, regardez le plan d'exécution. Vous pouvez voir qu’il associe les tables dans un ordre non optimal (par exemple, il pourrait associer b et c avant de rejoindre un qui possède la condition de filtre).

Vous pouvez utiliser des astuces pour modifier les chemins d'accès, l'ordre de jointure ou la méthode de jointure.

Mise à jour : la réponse au commentaire m'a conduit à this . présentation, qui pourrait être utile ou au moins intéressante.

Si vous dites qu'il n'y a pas d'index, cela signifie-t-il également qu'il n'y a pas de clé primaire ou étrangère définie? Évidemment, l'analyse des tables et la collecte de statistiques sont importantes, mais si les métadonnées telles que la définition de la manière dont les tables sont censées être jointes n'existent pas, Oracle peut choisir un chemin d'exécution médiocre.

Dans ce cas, l’utilisation d’un conseil tel que / * + ORDERED * / pourrait bien être la seule option permettant à l’optimiseur de choisir de manière fiable un bon chemin d’exécution. Il peut également être intéressant d’ajouter des clés étrangères et des clés primaires, mais de les définir comme DESACTIVER et VALIDER.

Je suppose que l’utilité de ce commentaire dépend de la mesure dans laquelle l’aversion pour les index va jusqu’à YMMV.

Créez d'abord une vue sur cette requête, puis générez une table à partir de cette vue. Créez également un index à la date, créez un travail et planifiez-le à minuit lorsque le système est inactif.

Etant donné que vous ne pouvez pas créer d'index, je m'assurerais que les statistiques sont toutes à jour, je réécrirais la requête de la manière suivante:

avec a (sélectionnez / * + MATERIALIZE * / ItemType, ItemNum, DateStored, ItemDate à partir d’éléments où ItemType est entre (112,115,189,241)) SELECT c.ClaimNumber, a.ItemDate, c.DTN, b.FilePath De, itempages b, keygroupdata c O a.ItemNum = b.ItemNum ET b.ItemNum = c.ItemNum ORDER BY a.DateStored DESC

Supprimer ORDER BY

effectuez le tri après avoir extrait les lignes dans votre application.

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