von Markus Winand.

Spalten zusammenfügen


In diesem Abschnitt geht es um eine Verschleierung, die vor allem bei mehrspaltigen Indizes auftritt.

Das erste Beispiel hat wieder mit Datums-Typen zu tun, steht aber vor dem umgekehrten Problem als zuvor. Die MySQL-Abfrage fügt getrennte Datums- und Zeit-Spalten zusammen:

SELECT ...
  FROM ...
 WHERE ADDTIME(date_column, time_column)
     > DATE_ADD(now(), INTERVAL -1 DAY)

Damit werden alle Zeilen der letzten 24 Stunden gesucht. Diese Abfrage kann einen zusammengesetzten Index auf (DATE_COLUMN, TIME_COLUMN) aber nicht verwenden, weil die Suche nicht auf den indizierten Spalten, sondern auf abgeleiteten Daten stattfindet.

Hinweis in eigener Sache

Ich biete SQL Schulungen, Optimierung und Beratung an. Auch der Kauf meines Buches „SQL Performance Explained“ (ab €9,95) unterstützt meine Arbeit an dieser Webseite.

Das Problem kann man vermeiden, indem man einen Daten-Typen wie DATETIME verwendet, der sowohl eine Datums- als auch eine Zeit-Kompo­nen­te hat. Die Abfrage kann sich dann direkt auf diese Spalte beziehen:

SELECT ...
  FROM ...
 WHERE datetime_column
     > DATE_ADD(now(), INTERVAL -1 DAY)

Wenn man vor diesem Problem steht, hat man aber nur in den seltensten Fällen die Möglichkeit die Tabelle umzubauen.

Die nächste Option ist, einen Funktions-basierten Index zu benutzen – wenn die verwendete Datenbank sie anbietet. Das hat natürlich alle Nachteile, die schon zuvor besprochen wurden. Bei diesem Beispiel arbeiten wir aber mit einer MySQL Datenbank – Funktions-basierte Indizierung ist ohnehin keine Option.

Dennoch hat man die Möglichkeit die Abfrage so zu formulieren, dass der zusammengesetzte Index auf DATE_COLUMN und TIME_COLUMN zumindest teilweise mit einem Zugriffsprädikat genutzt werden kann. Dazu muss man eine zusätzliche Bedingung auf DATE_COLUMN in die where-Klausel aufnehmen.

 WHERE ADDTIME(date_column, time_column)
     > DATE_ADD(now(), INTERVAL -1 DAY)
   AND date_column
    >= DATE(DATE_ADD(now(), INTERVAL -1 DAY))

Diese Bedingung ist, aus logischer Sicht, absolut redundant. Sie ist aber direkt auf der ersten Index-Spalte – kann also als Zugriffsprädikat genutzt werden. Diese Methode ist zwar nicht perfekt, aber als Annäherung meist gut genug.

Tipp

Verwende eine redundante Bedingung mit der höchstwertigen Spal­te, wenn eine Bereichs-Bedingung mehrere Spalten zusammen­fügt.

Verwendet bei PostgreSQL die Row-Values-Syntax.

Diese Methode kann man auch anwenden, wenn Datum und Zeit als Text gespeichert sind. Dafür muss eine lexikalische Sortierung allerdings eine chronologische Reihenfolge liefern – wie zum Beispiel von ISO 8601 vorgesehen (YYYY-MM-DD HH:MM:SS). Das folgende Beispiel demonstriert diese Methode mit der TO_CHAR-Funktion der Oracle Datenbank:

SELECT ...
  FROM ...
 WHERE date_string || time_string
     > TO_CHAR(sysdate - 1, 'YYYY-MM-DD HH24:MI:SS')
   AND date_string
    >= TO_CHAR(sysdate - 1, 'YYYY-MM-DD')

Der Problemstellung, mehrere Spalte auf einmal zu vergleichen, werden wir im Abschnitt Durch Ergebnisse blättern nochmal begegnen. Auch dort findet diese Annäherungsmethode Verwendung.

Manchmal muss man Bedingungen gezielt verschleiern, damit sie nicht als Zugriffsprädikat verwendet werden. Wir hatten dieses Problem bereits, als wir die Auswirkungen von Bind-Para­me­tern auf LIKE-Suchen behandelt haben. Dazu folgendes Beispiel:

SELECT last_name, first_name, employee_id
  FROM employees
 WHERE subsidiary_id = ?
   AND last_name LIKE ?

Angenommen es gibt sowohl einen Index auf SUBSIDIARY_ID als auch auf LAST_NAME. Welcher ist für diese Abfrage besser?

Ohne zu wissen, wo die Wildcard-Zeichen im Suchbegriff stehen, kann man keine qualifizierte Antwort geben. Auch die Datenbank kann nur raten. Wenn man aber weiß, dass der Suchbegriff immer mit einem Wildcard-Zeichen beginnt, kann man die entsprechende Bedingung absichtlich verschleiern. Dadurch kann der LIKE-Filter nicht als Zugriffsprädikat verwendet werden:

SELECT last_name, first_name, employee_id
  FROM employees
 WHERE subsidiary_id = ?
   AND last_name || '' LIKE ?

Dafür genügt es, einen leeren String an die Spalte LAST_NAME anzuhängen. Dieses Vorgehen ist allerdings nur der letzte Ausweg, wenn es keine anderen Möglichkeiten mehr gibt.

Vorherige SeiteNächste Seite

Du kannst nicht alles an einem Tag lernen. Abonniere den Newsletter via E-Mail, Twitter oder RSS um sukzessive aufzuholen. Und sieh dir auch modern-sql.com an.

Über den Autor

Foto von Markus Winand

Markus Winand gibt auf modern-sql.com Einblick in SQL und zeigt, wie es von verschiedenen Systemen unterstützt wird. Zuvor machte er use-the-index-luke.com, was er noch immer wartet. Markus kann als Trainer, Sprecher und Berater auf winand.at engagiert werden.

Sein Buch kaufen

Titelbild von „SQL Performance Explained“: Eichhörnchen läuft durchs Grass

Die Essenz: SQL-Tuning auf 200 Seiten

Jetzt Kaufen
(Taschenbuch und/oder PDF)

Sein Training

Markus verwandelt veraltetes SQL-92-Wissen in solides und zeitgemäßes SQL-Know-how

Erfahren Sie mehr»

Mit Markus Winand verbinden

Markus Winand auf LinkedInMarkus Winand auf XINGMarkus Winand auf Twitter
„Use The Index, Luke!“ von Markus Winand ist unter einer Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License lizenziert.
Impressum | Kontakt | KEINE GEWÄHR | Handelsmarken | Datenschutz und DSGVO | CC-BY-NC-ND 3.0 Lizenz