In MySQL wurde GROUP BY in der Vergangenheit auch für die Sortierung verwendet. Wenn eine Abfrage GROUP BY angegeben hat, wurde das Ergebnis so sortiert, als ob ORDER BY in der Abfrage vorhanden wäre.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
mysql-5.7> CREATE TABLE t (id INTEGER, cnt INTEGER);
Query OK, 0 rows affected (0.03 sek)
mysql-5.7> IN t-WERTE EINFÜGEN (4,1),(3,2),(1,4),(2,2),(1,1),(1,5),(2,6),(2,1),(1,3),(3,4),(4,5),(3,6);
Abfrage OK, 12 Zeilen betroffen (0,02 sek.)
Datensätze: 12 Duplikate: 0 Warnungen: 0
mysql-5.7> SELECT id, SUM(cnt) FROM t GROUP BY id;
+——+———-+
/ id / SUMME(cnt) |
+——+———-+
| 1 | 13 |
| 2 | 9 |
| 3 | 12 |
| 4 | 6 |
+——+———-+
4 Zeilen im Satz (0.00 sec)
|
MySQL here implicitly sorts the results from GROUP BY (i.e. in the absence of ASC
or DESC
designators for GROUP BY
columns ).
MySQL also supported explicit sorting with GROUP BY (i.e. by using explicit ASC
or DESC
designators for GROUP BY
columns).
1
2
3
4
5
6
7
8
9
10
|
mysql-5.7> SELECT id, SUM(cnt) FROM t GROUP BY id DESC;
+——+———-+
/ id / SUMME(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 2 | 9 |
| 1 | 13 |
+——+———-+
4 Zeilen im Satz, 1 Warnung (0,00 Sek.)
|
Dies hat sich in 8.0 geändert, da es keine implizite oder explizite Sortierung für GROUP BY mehr unterstützt. In diesem Blogbeitrag werde ich erklären, warum diese Änderung notwendig wurde und welche Arbeit als Vorläufer dieser Änderung geleistet wurde.
GROUP BY in MySQL
Um eine Reihe von Zeilen zu gruppieren, wählt MySQL Optimizer verschiedene Methoden aus. Eine davon besteht darin, die Zeilen zu sortieren, bevor sie gruppiert werden. Dies macht es einfach, eine Gruppe nach der anderen zu gruppieren. Es wird auch kostengünstig, wenn es einen Index gibt, mit dem sortierte Zeilen abgerufen werden können. Wenn kein Index vorhanden ist, kann MySQL Optimizer vor der Gruppierung weiterhin eine externe Sortierung (Filesort) durchführen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
mysql -5.7>ELECT SQL_BIG_RESULT id, SUM(cnt) FROM t GROUP BY id \G
*************************** 1. reihe ***************************
id: 1
select_type: EINFACH
Tabelle: t
Partitionen: NULL
Reihenfolge: ALLE
possible_keys: NULL
Schlüssel: NULL
key_len: NULL
ref: NULL
rows: 12
filtered: 100.00
Extra: Using filesort
1 row in set, 1 warning (0.01 sec)
mysql-5.7> ALTER TABLE t ADD INDEX (id, cnt);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql-5.7> EXPLAIN SELECT id, SUM(cnt) FROM t GROUP BY id \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
Typ: index
possible_keys: id
Schlüssel: id
key_len: 10
ref: NULL
Zeilen: 12
gefiltert: 100.00
Extra: Index verwenden
1 Zeile im Set, 1 Warnung (0.00 sec)
|
Wie im Beispiel gezeigt, verwendet MySQL vor dem Hinzufügen eines Index zur Tabelle eine externe Sortierung, um GROUP BY . Für die Beispielabfrage habe ich den Plan mithilfe von SQL_BIG_RESULT erzwungen (da MySQL diesen Plan nicht für den Datensatz auswählt, den wir haben). MySQL würde diesen Plan jedoch verwenden, um in Abwesenheit eines Index zu gruppieren, um sortierte Zeilen zu erhalten, und die Verwendung einer temporären Tabelle wird aufgrund der großen Anzahl von Gruppen kostspielig. Sobald der Index hinzugefügt wurde, wird der Index für GROUP BY verwendet.
Es ist jedoch nicht erforderlich, Zeilen vor dem Gruppieren sortiert zu haben. Der Optimierer kann dazu eine temporäre Tabelle verwenden. Jede Zeile in dieser Tabelle wäre eine gruppierte Zeile, und mit jeder eingehenden Zeile wird die Zeile aktualisiert, die dieser Gruppe in der Tabelle entspricht. Sortieren ist hier nicht erforderlich. Da jedoch erwartet wurde, dass GROUP BY in MySQL sortiert, war es auch in diesem Fall gezwungen, die gruppierten Zeilen zu sortieren.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
mysql-5.7> ALTER TABLE t DROP INDEX id;
Abfrage OK, 0 Zeilen betroffen (0,01 Sek.)
Datensätze: 0 Duplikate: 0 Warnungen: 0
mysql-5.7> EXPLAIN SELECT id, SUM(cnt) FROM t GROUP BY id \G
*************************** 1. reihe ***************************
id: 1
select_type: EINFACH
Tabelle: t
Partitionen: NULL
Typ: ALLE
possible_keys: NULL
Schlüssel: NULL
key_len: NULL
ref: NULL
Zeilen: 12
gefiltert: 100.00
Extra: Using temporary; Using filesort
1 Zeile im Set, 1 Warnung (0.00 sec)
|
In der Beispielabfrage können wir sehen, dass MySQL, obwohl temporäre Tabelle verwendet wird, immer noch eine externe Sortierung durchführt. Benutzer müssten ORDER BY NULL explizit angeben, damit MYSQL weiß, dass GROUP BY nicht sortiert werden muss. Daher war eine nicht standardmäßige Syntax (ORDER BY NULL ) erforderlich, um dem Effekt einer anderen nicht standardmäßigen Erweiterung (GROUP BY sorting) entgegenzuwirken. Es ist jetzt viel sauberer, da wir diese Unordnung beseitigt haben.
Entfernen der impliziten Sortierung für GROUP BY
Vor einiger Zeit habe ich versucht, Fehler 71804 zu beheben. Der Reporter erwartete, dass MySQL die unnötige Dateisortierung für GROUP BY nicht durchführte. Der Versuch, einen Patch für den Fehler zu erstellen, hat uns klar gemacht, dass die Optimierung dieser speziellen Situation aufgrund der Unterstützung für die implizite und explizite Sortierung dieser GRUPPE NACH nicht sehr einfach ist. Daher kamen wir zu dem Schluss, dass wir Code, der sich auf das Sortieren nach GROUP BY bezieht, neu berücksichtigen sollten, bevor diese Optimierung durchgeführt werden kann.
Der erste Schritt dabei war, die implizite Sortierung für GROUP BY zu entfernen. Wie im Benutzerhandbuch hier erwähnt, wurde beschlossen, die Unterstützung dafür vor einiger Zeit zu entfernen . Es wurde als Teil der Funktion descending Index in 8.0 erstellt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
mysql> WÄHLEN SIE id, SUMME(cnt) AUS t GRUPPE NACH ID;
+——+———-+
/ id / SUMME(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 1 | 13 |
| 2 | 9 |
+——+———-+
4 Zeilen im Satz (0,00 Sek.)
mysql>ELECT id, SUM(cnt) FROM t GROUP BY id \G
*************************** 1. reihe ***************************
id: 1
select_type: EINFACH
Tabelle: t
Partitionen: NULL
Typ: ALLE
possible_keys: NULL
Schlüssel: NULL
key_len: NULL
ref: NULL
Zeilen: 12
gefiltert: 100.00
Extra: Temporär verwenden
1 Zeile im Set, 1 Warnung (0.00 sec)
|
Wie im obigen Beispiel gezeigt, wird für die Abfrage keine Sortierung durchgeführt. Daher werden gruppierte Zeilen im Endergebnis nicht sortiert. Wenn Benutzer sortierte Zeilen benötigen, müssen sie ORDER BY in der Abfrage angeben.
In MySQL 5.7 und unten versionen, benutzer finden die folgenden warnung in die manuelle.
„GROUP BY
sortiert implizit standardmäßig (dh ohne ASC
oder DESC
Bezeichner für GROUP BY
Spalten). Verlassen Sie sich jedoch auf implizite GROUP BY
Sortierung (dh Sortierung in Abwesenheit von ASC
oder DESC
Bezeichnern) oder explizite Sortierung für GROUP BY
(dh durch explizite ASC
oder DESC
Bezeichner für GROUP BY
Spalten) ist veraltet. Um eine bestimmte Sortierreihenfolge zu erstellen, geben Sie eine ORDER BY
-Klausel an. „
Entfernen der expliziten Sortierung für GROUP BY
Wenn es darum ging, die explizite Sortierung zu entfernen, war es etwas schwieriger. Wir konnten es nicht entfernen, es sei denn, MySQL unterstützte ORDER BY mit ROLLUP. ROLLUP mit ORDER BY war in MySQL 5.7 und früheren Versionen nicht erlaubt. Alternativ würden Benutzer GROUP BY ASC / DESC verwenden, um sortierte Daten mit ROLLUP abzurufen (obwohl die Sortierung sehr restriktiv war, da Superaggregatzeilen immer nach den Zeilen platziert wurden, die bei ASC zur Berechnung verwendet wurden, und umgekehrt für DESC). Wir mussten diese Einschränkung aufheben, bevor wir die Unterstützung für die explizite Sortierung für GROUP BY entfernt haben.
MySQL erlaubt jetzt ORDER BY mit ROLLUP. Ich habe hier ausführlich erklärt, wie Sie diese Verbesserung nutzen können. Wie im selben Blog erläutert, sollten Benutzer, die genau die gleiche Sortierreihenfolge für Nullen wie MySQL 5.7 für das ROLLUP wünschen, die Funktion GROUPING () verwenden, um die Abfrage auf einfache Weise neu zu schreiben.
Kurz gesagt, wir haben die folgenden Dinge als Vorläufer zum Entfernen der expliziten Sortierung für GROUP BY getan.
1. Hinzufügen der Funktion GROUPING()
2. Entfernen der impliziten Sortierung für GROUP BY
3. Zulassen von ORDER BY mit ROLLUP
Und schließlich haben wir die explizite Sortierung für GROUP BY in MySQL 8.0.13 entfernt.
Wir haben vor einiger Zeit nach der Meinung der Community gefragt. Wir kamen zu dem Schluss, dass Benutzer, die sich dieser von MySQL bereitgestellten nicht standardmäßigen Erweiterung bewusst waren, damit einverstanden waren.
Fazit
Obwohl wir noch etwas zu tun haben, bevor wir Bug 71804 beheben, sind wir froh, dass wir das geschafft haben. Bitte teilen Sie uns Ihre Meinung mit. Vielen Dank für die Verwendung von MySQL!