MySQL unterstützen

Oft hat man das Problem, dass man unbedingt möchte, dass ein bestimmter Index genutzt wird. Hat man die Tipps im letzten Beitrag befolgt und der Index wird laut EXPLAIN-Befehl immernoch nicht benutzt, wundert man sich. Als nächster Schritt wird brachiale Gewalt angewendet, indem man FORCE INDEX benutzt, damit auch garantiert der gewünschte Index genutzt wird. Aber MySQL verweigert weiterhin. Warum nur? Weil die SELECT-Anfrage genau das verhindert! Ich möchte hier ein paar typische Fehler aufzeigen und wie man es besser macht, damit Indizes überhaupt genutzt werden können und die Abfragen erheblich schneller abgearbeitet werden können.

Problem: Wir möchten Datensätze finden, an deren Anfang der String ‚Auto‘ steht (beispielsweise Automobil, Auto, Automat).
Schlecht:

SELECT *
FROM tabelle
WHERE SUBSTR(spalte,1,4)='Auto'

Gut:

SELECT *
FROM tabelle
WHERE spalte LIKE 'Auto%'

Problem ist hier, dass auf Funktionen kein Index angewendet werden kann. Wenn man allerdings eine Suche mit LIKE durchführt, kann ein Index sehr wohl benutzt werden – das geht allerdings nur, wenn man mit LIKE den Wortbeginn festlegt. Bei LIKE ‚%Auto%‘ kann wiederum kein Index genutzt werden. Das ist in der Natur binärer Suchbäume begründet.

Achtung, wichtige Regl: In einem Index gespeicherte Werte können darüber Auskunft geben, welche Werte in einer Spalte vorhanden sind, er weiß allerdings nicht, welche Werte in der Spalte nicht vorhanden sind!
Schlecht:

SELECT *
FROM tabelle
WHERE spalte!=0

Damit für eine solche Abfrage ein Index benutzt werden kann, müssen wir die Werte einbeziehen, die es gibt:

SELECT *
FROM tabelle
WHERE spalte>0
-- AND spalte<0

Die zweite Bedingung ist abhängig davon, ob es sich bei der Spalte um einen UNSIGNED INT (oder Float) handelt oder nicht.

Eine arithmetische Rechnung kann ebenfalls nicht für einen Index genutzt werden. Deshalb schlecht:

SELECT *
FROM tabelle
WHERE menge + 3000 < 5000

Besser ist es, bereits in der Anwendung bzw. vor der Query die Ungleichung sozusagen in die Variable (die Tabellenspalte) und die Konstante zu zerlegen und somit als performantere Variante folgendes zu nutzen:

SELECT *
FROM tabelle
WHERE menge < 2000

Außerdem kann ein Index nicht verwendet werden, wenn auf beiden Seiten einer Bedingung eine Spalte (kann auch die gleiche sein) derselben Tabelle verwendet wird. Das ist allerdings nicht immer ganz leicht zu umgehen.
Zu diesem Punkt fällt mir leider gerade kein praxisnahes Beispiel ein.

Was in anderen DBMS als MySQL durchaus zu Problemen führen kann, ist die Verwendung von unterschiedlichen Datentypen beim Vergleich. Wenn die Spalte vom Typ INT ist und man per WHERE spalte='123' abfragt, kann mitunter der Index nicht benutzt werden, da 123 als String zum Vergleich übergeben wurde. Bei MySQL ist das aber kein Problem, da es einen automatischen Cast vornimmt und somit WHERE spalte=123 ausführt.

Ich hoffe nun können sie sich wieder mit ihrer Datenbank anfreunden, die manchmal ganz störrisch verweigert, einen bestimmten Index einzusetzen 😉

Jan hat 152 Beiträge geschrieben

2 Kommentare zu “MySQL unterstützen

  1. Denis sagt:

    „Außerdem kann ein Index nicht verwendet werden, wenn auf beiden Seiten einer Bedingung eine Spalte (kann auch die gleiche sein) derselben Tabelle verwendet wird. Das ist allerdings nicht immer ganz leicht zu umgehen. Zu diesem Punkt fällt mir leider gerade kein praxisnahes Beispiel ein.

    Ich habe eins. 🙂

    Situation:
    – Wir haben eine Flirtseite mit vielen Usern
    – Die User können untereinander angeben, wer ihr „Partner“ (Beziehung) ist.
    – Dies soll aber nru angezeigt werden,
    wenn der genannte Partner den User ebenfalls angegeben hat.

    Unsere Lösung:
    – Wir haben in der Tabelle für User eine Spalte partner_fs (Fremdschlüssel) eingefügt.

    SQL:

    SELECT
    this.id,
    this.username,
    partner.id,
    partner.username
    FROM
    usertabelle this,
    usertabelle partner
    WHERE
    this.id = WERT_FUER_USER_ID
    AND this.single_status = KEIN_SINGLE
    AND this.partner_fs > 0
    AND partner.id = this.partner_fs
    AND partner.id != this.id
    AND partner.single_status = KEIN_SINGLE
    AND this.id = partner.partner_fs

    Das SQL ist natürlich gekürzt und verallgemeinert. 😉

    Bisher hat sich diese Lösung auch unter hoher Last sehr bewährt. 🙂

    lg,
    Denis

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>