Der Oracle Optimizer ist ziemlich gut darin, brauchbare Ausführungspläne für SQL zu erzeugen, aber manchmal hat er einfach zu wenig Informationen, und in diesen Fällen, ist er darauf angewiesen zu raten. Dies trift zum Beispeispiel auf where Klauseln zu, die ein like '%irgendwas%' enthalten. Oracle kann hat keine belastbaren Informationen darüber, ob diese Einschränkungen, die Daten auf 90%, 1% oder 0,01% reduziert. Also macht der Optimizer einfach die Annahme, dass die Datenmenge durch diese Einschränkung auf 5% reduziert wird und basiert darauf sämtliche weitere Entscheidungen, insbesondere ob ein Index zu verwenden ist oder nicht.
Konkret hieß das bei mir, dass für das folgende Statement:
SELECT * FROM tabelle WHERE text LIKE '%eistell%';
der Optimizer beschloss, dass bei 5% Restdaten sich der Index nicht lohnte, der auf der fraglichen Spalte war und verwendete einen Full Table Scan. In Wirklichkeit war die Einschränkung sehr viel größer. Durch die where-Klausel wurde das Datenvolumen auf ca 0,1% eingedampft, so dass sich ein Indexscan ganz bestimmt lohnen würde. Dies ließ sich auch verifizieren, in dem man entsprechende Index Hints verwendete. Das Problem war jedoch: Das SQL-Statement wurde dynamisch generiert und konnte die LIKE-Klausel mal enthalten, mal aber auch nicht. Ich konnte nur die Tabelle durch einen geeigneten View ersetzen. Dieser durfte aber nicht fix einen entsprechenden Index Hint verwenden, da dann Statements ohne die LIKE Klausel exorbitant langsam werden würden. Allenfalls generelle Hints, wie FIRST_ROWS oder dergleichen waren akzeptabel.
Die Lösung, die ich schließlich fand (genau genommen fand sie Tom Kyte) war ein fieser kleiner Join:
select /*+ first_rows(100) */ t1.a, t1.b, t2.text
from tabelle t1, tabelle t2
where t1.rowid = t2.rowid
Dieser Join ist vom Ergebnis her identisch mit der vorherigen Tabelle, aber auf die Tabelle wird zweimal zugegriffen, einmal für die text-Spalte und einmal für alle anderen Spalten. Dies führt bei vielen Select zu geringen Perfomanceeinbußen. Aber im Falle der oben beschriebenen LIKE-Klausel merkt der Optimizer nur, dass er von t2 nur rowid und text benötigt und wird, wenn text eingeschränkt wird immer auf den Index zugreifen. Was einen dramatischen Performancegewinn zur Folge hatte.
Die Essenz aus diesem Trick:
Neben den bekannten Hints zur Manipulation des Oracle Optimizer kann man ihn auch durch Joins (oder subselects, wie in der ursprünglichen Antwort von Tom Kyte) dazu bringen bestimmte Indizes zu verwenden, wenn ein 'harter' Hint nicht verwendet werden kann.
Die anderen Teile dieser Serie findet ihr hier:
Talks
Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: