Teil 2: MySQL 4 - Optimierung der Indizes in Abfragen

SQL-Optimierung: Indexnutzung in Queries manuell steuern

Versuchen Sie, innerhalb von Vergleichen die indizierten Spalten unabhängig zu verwenden

Wenn Sie eine Spalte in einem Funktionsaufruf oder als Komponente eines komplexeren Terms in einem arithmetischen Ausdruck verwenden, kann MySQL den Index nicht nutzen, weil es den Wert des Ausdrucks für jede Zeile berechnen muss. Manchmal ist dies unvermeidbar, aber häufig kann man eine Anfrage auch so umformulieren, dass die Spalte unabhängig von anderen Komponenten verwendet wird.

Die folgenden WHERE-Klauseln zeigen, wie es geht; sie sind arithmetisch äquivalent, aus Sicht der Anfrageoptimierung jedoch völlig unterschiedlich. In der ersten Zeile vereinfacht die Anfrageoptimierung den Ausdruck 4/2 zum Wert 2 und wendet dann einen Index auf mycol an, um schnell alle Werte kleiner 2 zu finden. Im zweiten Ausdruck muss MySQL den Wert von mycol für jede Zeile ermitteln, ihn mit 2 multiplizieren und das Ergebnis dann mit 4 vergleichen. Es kann kein Index verwendet werden, weil jeder Wert in der Spalte ermittelt werden muss, damit der Ausdruck auf der linken Seite des Vergleichs ausgewertet werden kann:

WHERE mycol < 4 / 2
WHERE mycol * 2 < 4

Betrachten wir noch ein Beispiel. Angenommen, Sie haben die indizierte Spalte date_col. Bei einer Anfrage wie der folgenden wird der Index nicht genutzt:

SELECT * FROM mytbl WHERE YEAR(date_col) < 1990;

Der Ausdruck führt keinen Vergleich einer indizierten Spalte mit dem Wert 1990 durch; er vergleicht einen aus dem Spaltenwert berechneten Wert, und dieser Wert muss für jede einzelne Zeile berechnet werden. Der Index für date_col kann also nicht genutzt werden. Gibt es eine Lösung? Man verwendet einfach ein konkretes Datum, dann kann der Index für date_col genutzt werden:

WHERE date_col < ‘1990-01-01’

Aber was tun Sie, wenn Sie kein bestimmtes Datum haben? Vielleicht wollen Sie stattdessen alle Datensätze finden, deren Datum innerhalb einer bestimmten Anzahl von Tagen ab dem heutigen Tag liegt. Es gibt mehrere Möglichkeiten, eine solche Anfrage zu formulieren, die aber nicht alle gleich gut sind. Hier sehen Sie drei der Möglichkeiten:

WHERE TO_DAYS(date_col) - TO_DAYS(CURDATE()) < cutoff
WHERE TO_DAYS(date_col) < cutoff + TO_DAYS(CURDATE())
WHERE date_col < DATE_ADD(CURDATE(), INTERVAL cutoff DAY)

Für die erste Zeile wird kein Index verwendet, weil die Spalte für jede Zeile ermittelt werden muss, damit der Wert von TO_DAYS(date_col) berechnet werden kann. Die zweite Zeile ist besser. Sowohl cutoff als auch TO_DAYS(CURDATE) sind Konstanten, die rechte Seite des Vergleichs kann also von der Optimierung vor der Verarbeitung der Anfrage einmalig (statt einmal pro Zeile) berechnet werden. Allerdings kommt die Spalte date_col auch hier im Funktionsaufruf vor, weshalb der Index nicht genutzt wird. Die dritte Zeile stellt die Lösung der Wahl dar. Auch hier kann die rechte Seite des Vergleichs als Konstante einmal berechnet werden, bevor die Anfrage ausgeführt wird, aber jetzt ist der Wert ein Datum. Dieser Wert kann direkt mit den Werten von date_col verglichen werden, die jetzt nicht mehr in Tage umgewandelt werden müssen. In diesem Fall kann der Index genutzt werden.