Dieser Abschnitt enthält die create, insert und select Kommandos für den „3-Minuten Test“. Mach den Test doch selbst, bevor du weiter liest.
Die create und insert Anweisungen findest du in diesem Download.
Die Ausführungspläne sind zur besseren Lesbarkeit abgekürzt.
Frage 1 — DATE Anti-Pattern
CREATE INDEX tbl_idx ON tbl (date_column);SELECT COUNT(*)
FROM tbl
WHERE EXTRACT(YEAR FROM date_column) = 2024;SELECT COUNT(*)
FROM tbl
WHERE date_column >= DATE'2024-01-01'
AND date_column < DATE'2018-01-01';Der erste Ausführungsplan liest den ganzen Index (type=index). Der zweite Ausführungsplan liest nur die relevanten Zeilen aus dem Index (type=range). Beachte, dass die Zeilenangabe den Effizienzunterschied ebenfalls widerspiegelt.
+-------+---------------+---------+------+--------------------------+
| type | possible_keys | key | rows | Extra |
+-------+---------------+---------+------+--------------------------+
| index | NULL | tbl_idx | 300 | Using where; Using index |
+-------+---------------+---------+------+--------------------------++-------+---------------+---------+------+--------------------------+
| type | possible_keys | key | rows | Extra |
+-------+---------------+---------+------+--------------------------+
| range | tbl_idx | tbl_idx | 271 | Using where; Using index |
+-------+---------------+---------+------+--------------------------+Frage 2 — Indiziertes Top-N
CREATE INDEX tbl_idx ON tbl (a, date_column);SELECT *
FROM tbl
WHERE a = 12
ORDER BY date_column DESC
LIMIT 1;Die Abfrage nutzt die Zugriffsmethode (type) REF – ähnlich einem range scan. Beachte vor allem, dass es keine Sortieroperation in der Extra Spalte gibt.
+------+---------------+---------+-------------+
| type | possible_keys | key | Extra |
+------+---------------+---------+-------------+
| ref | tbl_idx | tbl_idx | Using where |
+------+---------------+---------+-------------+Frage 3 — Spaltenreihenfolge
CREATE INDEX tbl_idx ON tbl (a, b);SELECT *
FROM tbl
WHERE a = 38
AND b = 1;SELECT *
FROM tbl
WHERE b = 1;DROP INDEX tbl_idx ON tbl;CREATE INDEX tbl_idx ON tbl (b, a);SELECT *
FROM tbl
WHERE a = 38
AND b = 1;SELECT *
FROM tbl
WHERE b = 1;Die erste Abfrage kann beide Indizes optimal nutzen:
+------+---------------+---------+------+-------+
| type | possible_keys | key | rows | Extra |
+------+---------------+---------+------+-------+
| ref | tbl_idx | tbl_idx | 1 | NULL |
+------+---------------+---------+------+-------++------+---------------+---------+------+-------+
| type | possible_keys | key | rows | Extra |
+------+---------------+---------+------+-------+
| ref | tbl_idx | tbl_idx | 1 | NULL |
+------+---------------+---------+------+-------+Die zweite Abfrage kann den ersten Index nicht effizient nutzten und liest stattdessen die ganze Tabelle (type=ALL). Wenn die Spalten im Index umgedreht werden, können beide Abfragen optimal vom Index profitieren (Type=REF bzw. Range).
+------+---------------+------+------+-------------+
| type | possible_keys | key | rows | Extra |
+------+---------------+------+------+-------------+
| ALL | NULL | NULL | 300 | Using where |
+------+---------------+------+------+-------------++------+---------------+---------+------+-------+
| type | possible_keys | key | rows | Extra |
+------+---------------+---------+------+-------+
| ref | tbl_idx | tbl_idx | 2 | NULL |
+------+---------------+---------+------+-------+Beachte, dass MySQL auch einen Full-Index-Scan (type=Index) durchführen könnte. Obwohl das besser sein kann als ein Full-Table-Scan, ist es noch immer schlechter als ein Index-Range-Scan (type=ref oder range)
Frage 4 — LIKE
CREATE INDEX tbl_idx ON tbl (text);SELECT *
FROM tbl
WHERE text LIKE 'TJ%';Der Ausführungsplan zeigt klar, dass ein Index-Range-Scan durchgeführt wird (type=range). Da der Suchbegriff 'TERM%' nur ganz am ende ein Wildcard-Zeichen hat, kann der komplette Suchbegriff als Index-Zugriffsprädikat genutzt werden.
+-------+---------+---------+------+-----------------------+
| type | key | key_len | ref | Extra |
+-------+---------+---------+------+-----------------------+
| range | tbl_idx | 258 | NULL | Using index condition |
+-------+---------+---------+------+-----------------------+Lerne mehr darüber
Frage 5 — Index Only Scan
CREATE INDEX tbl_idx ON tbl (a, date_column)SELECT date_column, count(*)
FROM tbl
WHERE a = 38
GROUP BY date_columnSELECT date_column, count(*)
FROM tbl
WHERE a = 38
AND b = 1
GROUP BY date_columnDie erste Abfrage nutzt den Index, um auf der Spalte A zu suchen, kann aber auch die selektierte Spalte DATE_COLUMN aus dem Index lesen (Extra=Using Index). Die zweite Abfrage muss zusätzlich in die Tabelle sehen, um den Filter auf der Spalte B zu prüfen. Obwohl dieser Zugriff das Ergebnis schmälert, wird die Abfrage viel langsamer.
+------+---------------+---------+--------------------------+
| type | possible_keys | key | Extra |
+------+---------------+---------+--------------------------+
| ref | tbl_idx | tbl_idx | Using where; Using index |
+------+---------------+---------+--------------------------++------+---------------+---------+------------------------------------+
| type | possible_keys | key | Extra |
+------+---------------+---------+------------------------------------+
| ref | tbl_idx | tbl_idx | Using index condition; Using where |
+------+---------------+---------+------------------------------------+
