En MySQL, históricamente el GRUPO POR también se utilizó para proporcionar la clasificación. Si una consulta especificaba AGRUPAR POR, el resultado se ordenaba como si ORDEN POR estuviera presente en la consulta.
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 seg)
mysql-5.7> INSERTAR EN los VALORES de t (4,1),(3,2),(1,4),(2,2),(1,1),(1,5),(2,6),(2,1),(1,3),(3,4),(4,5),(3,6);
Query OK, 12 filas afectadas (0.02 sec)
Registros: 12 Duplicados: 0 Advertencias: 0
mysql-5.7> SELECT id, SUM(cnt) a partir DE t GROUP BY id;
+——+———-+
| id | SUMA(cnt) |
+——+———-+
| 1 | 13 |
| 2 | 9 |
| 3 | 12 |
| 4 | 6 |
+——+———-+
4 filas en set (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 | SUMA(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 2 | 9 |
| 1 | 13 |
+——+———-+
4 filas en conjunto, advertencia 1 (0.00 sec)
|
Esto ha cambiado en 8.0 como ya no admite ya sea implícita o explícita, de clasificación para el GRUPO. En esta entrada de blog, explicaré por qué este cambio se hizo necesario y también el trabajo realizado como precursor de este cambio.
AGRUPAR POR en MySQL
Para agrupar un conjunto de filas, MySQL optimizer elige diferentes métodos. Una de ellas es ordenar las filas antes de agruparlas. Esto hace que sea fácil agrupar un grupo tras otro. También se vuelve barato si hay un índice que se pueda usar para ordenar filas. Si no hay índice, MySQL optimizer aún podría decidir hacer una clasificación externa (filesort) antes de agrupar.
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> explain SELECT SQL_BIG_RESULT id, SUM(cnt) a partir DE t GROUP BY id \G
*************************** 1. fila ***************************
con id: 1
select_type: SIMPLE
tabla: t
particiones: NULL
orden: TODOS
possible_keys: NULL
clave: 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
tipo: índice
teclas posibles: id
clave: id
clave: 10
ref: NULL
filas: 12
filtrado: 100.00
Extra: Uso de índice
1 fila en el conjunto, 1 advertencia (0.00 seg)
|
Como se ve en el ejemplo, antes de agregar index a la tabla, MySQL utiliza una ordenación externa para AGRUPAR. Para la consulta de ejemplo, he forzado el plan usando SQL_BIG_RESULT (ya que MySQL no elegirá este plan para el conjunto de datos que tenemos). Pero MySQL usaría este plan para agrupar en ausencia de un índice para obtener filas ordenadas y el uso de tablas temporales se vuelve costoso debido a la gran cantidad de grupos. Una vez que se agrega el índice, recurre al índice para AGRUPAR POR.
Pero tener filas ordenadas antes de agruparlas no es una necesidad. Optimizador puede decidir hacer uso de una tabla temporal para hacerlo. Cada fila de esta tabla sería una fila agrupada y con cada fila entrante, se actualizará la fila correspondiente a ese grupo de la tabla. La clasificación no es necesaria aquí. Sin embargo, como se esperaba que el GRUPO POR en MySQL ordenara, se vio obligado a ordenar las filas agrupadas incluso en este caso .
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;
Query OK, 0 rows affected (0.01 sec)
Registros: 0 Duplicados: 0 Advertencias: 0
mysql-5.7> explain SELECT id, SUM(cnt) a partir DE t GROUP BY id \G
*************************** 1. fila ***************************
con id: 1
select_type: SIMPLE
tabla: t
particiones: NULL
tipo: TODOS
possible_keys: NULL
clave: NULL
key_len: NULL
ref: NULL
filas: 12
filtrado: 100.00
Extra: Usando temporal; Usando filesort
1 fila en conjunto, 1 advertencia (0.00 seg)
|
En la consulta de ejemplo, podemos ver que, aunque la tabla temporal se utiliza, MySQL todavía hace la ordenación externa. Los usuarios tendrían que especificar explícitamente ORDER BY NULL para que MYSQL sepa que el GRUPO BY no necesita ordenarse. Por lo tanto, se necesitaba una sintaxis no estándar (ORDEN POR NULL) para contrarrestar el efecto de otra extensión no estándar (AGRUPAR ordenando). Es mucho más limpio ahora que hemos eliminado ese desorden.
Eliminación de la clasificación implícita para GRUPOS POR
Hace algún tiempo estaba tratando de corregir el error 71804. El reportero esperaba que MySQL no hiciera la ordenación innecesaria de archivos que estaba haciendo para AGRUPAR POR. Intentar hacer un parche para el error nos hizo darnos cuenta de que optimizar esta situación en particular no es muy sencillo debido al soporte para la clasificación implícita y explícita de ese GRUPO. Así que llegamos a la conclusión de que antes de que se pudiera hacer esta optimización, deberíamos volver a factorizar el código relacionado con la ordenación para AGRUPAR POR.
El primer paso para hacerlo fue eliminar la ordenación implícita para AGRUPAR POR. Como se menciona en el manual de usuario aquí, se decidió quitar el soporte para él hace algún tiempo . Se ha hecho como parte de la función de índice descendente en 8.0.
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> SELECT id, SUM(cnt) a partir DE t GROUP BY id;
+——+———-+
| id | SUMA(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 1 | 13 |
| 2 | 9 |
+——+———-+
4 filas en set (0.00 sec)
mysql> explain SELECT id, SUM(cnt) a partir DE t GROUP BY id \G
*************************** 1. fila ***************************
con id: 1
select_type: SIMPLE
tabla: t
particiones: NULL
tipo: TODOS
possible_keys: NULL
clave: NULL
key_len: NULL
ref: NULL
hileras: 12
filtered: 100.00
Extra: El uso temporal
1 fila en conjunto, advertencia 1 (0.00 sec)
|
Como se ve en el ejemplo anterior, la clasificación no se realiza la consulta. Como resultado, las filas agrupadas no se ordenan en el resultado final. Si los usuarios necesitan filas ordenadas, deben especificar el ORDEN POR en la consulta.
En MySQL 5.7 y versiones inferiores, los usuarios encuentran la siguiente advertencia en el manual.
«GROUP BY
implícitamente el tipo por defecto (es decir, en la ausencia de ASC
o DESC
designadores de GROUP BY
columnas). Sin embargo, confiando en implícito GROUP BY
clasificación (es decir, la clasificación en la ausencia de ASC
o DESC
designadores) o explícita de la clasificación GROUP BY
(que es, mediante el uso explícito ASC
o DESC
designadores de GROUP BY
columnas) está en desuso. Para producir un orden de clasificación dado, proporcione una cláusula ORDER BY
. «
Eliminación de la ordenación explícita para GRUPOS POR
Cuando se trataba de eliminar la ordenación explícita, era un poco más complicado hacerlo. No podríamos eliminarlo a menos que MySQL soportara el ORDEN POR con ROLLUP. El ROLLUP con ORDER BY no estaba permitido en MySQL 5.7 y versiones anteriores. Por lo tanto, como alternativa, los usuarios usarían GROUP BY ASC/DESC para obtener datos ordenados con ROLLUP (Aunque la ordenación era muy restrictiva con filas de superagregados siempre colocadas después de las filas utilizadas para calcularlas en el caso de ASC y viceversa para DESC). Tuvimos que eliminar esta limitación antes de eliminar el soporte para la ordenación explícita para AGRUPAR POR.
MySQL ahora permite ORDENAR por con ROLLUP. He explicado en detalle cómo hacer uso de esta mejora aquí. Como se explica en el mismo blog, si los usuarios desean exactamente el mismo orden de clasificación de NULOs que el de MySQL 5.7 para ROLLUP, deben usar la función GROUPING () para reescribir la consulta de una manera simple.
En resumen, hemos hecho las siguientes cosas como precursores para eliminar la clasificación explícita para GROUP BY.
1. Adición de la función GROUPING ()
2. Eliminación de la ordenación implícita para el GRUPO POR
3. Permitiendo ORDENAR POR con ROLLUP
Y finalmente hemos eliminado la ordenación explícita para AGRUPAR POR en MySQL 8.0.13.
Pedimos la opinión de las comunidades hace algún tiempo. Llegamos a la conclusión de que los usuarios que estaban al tanto de esta extensión no estándar que MySQL proporcionaba estaban de acuerdo con que desapareciera.
Conclusión
Aunque todavía tenemos un poco más de trabajo por hacer antes de corregir el error 71804, estamos felices de haberlo hecho. Por favor, háganos saber sus pensamientos. ¡Gracias por usar MySQL!