J'ai une table avec 250K lignes dans ma base de données de test. (Il y a quelques centaines de millions en production, nous pouvons y observer le même problème.) La table a un identifiant de chaîne nvarchar2 (50), non nul, avec un index unique (ce n'est pas le PK).
Les identifiants sont constitués d'une première partie qui a 8 valeurs différentes dans ma base de données de test (et environ un millier en production), puis un signe @ et enfin un nombre de 1 à 6 chiffres. Par exemple, il peut y avoir 50 000 lignes commençant par «ABCD_BGX1741F_2006_13_20110808.xml @», suivies de 50 000 nombres différents.
Lorsque je recherche une seule ligne en fonction de son identifiant, la cardinalité est estimée à 1, le coût est très faible, cela fonctionne très bien. Lorsque je recherche plusieurs lignes avec plusieurs identifiants dans une expression IN ou une expression OR, les estimations de l'index sont complètement fausses, donc une analyse complète de la table est utilisée. Si je force l'index avec un indice, c'est très rapide, le scan complet de la table est en fait exécuté un ordre de grandeur plus lent (et beaucoup plus lent en production). C'est donc un problème d'optimiseur.
Comme test, j'ai dupliqué la table (dans le même schéma + espace de table) avec exactement le même DDL et exactement le même contenu. J'ai recréé l'index unique sur la première table pour faire bonne mesure et créé exactement le même index sur la table de clonage. J'ai fait un DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
. Vous pouvez même voir que les noms d'index sont consécutifs. Alors maintenant, la seule différence entre les deux tables est que la première a été chargée dans un ordre aléatoire sur une longue période, avec des blocs dispersés sur le disque (dans un espace de table avec plusieurs autres grandes tables), la seconde a été chargée en une seule fois INSERT-SELECT. A part ça, je ne peux imaginer aucune différence. (Le tableau d'origine a été réduit depuis la dernière grande suppression, et il n'y a pas eu une seule suppression après cela.)
Voici les plans de requête pour les malades et la table des clones (les chaînes sous le pinceau noir sont les mêmes sur toute l'image, et également sous leur pinceau gris.):
(Dans cet exemple, 1867 lignes commencent par l'identifiant noir. Une requête à 2 lignes produit une cardinalité de 1867 * 2, une requête à 3 lignes produit une cardinalité de 1867 * 3, etc. être une coïncidence, Oracle semble ne pas se soucier de la fin des identifiants.)
Qu'est-ce qui pourrait provoquer ce comportement? Évidemment, il serait assez coûteux de recréer la table en production.
USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg J'ai uniquement changé le schéma et le nom de l'espace disque logique . Vous pouvez voir que les noms de table et d'index sont les mêmes que sur la capture d'écran du plan de requête.