no MySQL, historicamente o GROUP BY também foi usado para fornecer ordenação. Se uma consulta especificada por, o resultado foi ordenado como se a ordem por estivesse presente na 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> INSERT INTO t VALORES (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 linhas afetadas (0.02 sec)
Registros: 12 Duplicates: 0 Warnings: 0
mysql-5.7> SELECT id, SOMA(cnt) a PARTIR de t GROUP BY id;
+——+———-+
| id | SOMA(cnt) |
+——+———-+
| 1 | 13 |
| 2 | 9 |
| 3 | 12 |
| 4 | 6 |
+——+———-+
4 linhas em conjunto (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 | SOMA(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 2 | 9 |
| 1 | 13 |
+——+———-+
4 linhas, em conjunto, 1 de aviso (0.00 sec)
|
Este foi alterado na versão 8.0 como ele não suporta mais tanto implícito ou explícito de classificação para agrupar POR. Neste post, vou explicar por que essa mudança se tornou necessária e também o trabalho feito como um precursor para esta mudança.
GROUP BY in MySQL
para agrupar um conjunto de linhas, MySQL optimizer escolhe diferentes métodos. Uma delas é classificar as linhas antes de agrupá-las. Isto torna mais fácil agrupar um grupo após o outro. Também se torna barato se houver um índice que poderia ser usado para começar linhas ordenadas. Se não houver índice, o Optimizador de MySQL poderá ainda decidir fazer a ordenação 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> EXPLICAR SELECIONE SQL_BIG_RESULT identificação, SOMA(cnt) a PARTIR de t GROUP BY id \G
*************************** 1. linha ***************************
id: 1
select_type: SIMPLES
tabela: t
partições: NULL
ordem: TODOS
possible_keys: NULL
chave: 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
possible_keys: id
chave: id
key_len: 10
ref: NULL
linhas: 12
filtrado: 100.00
Extra: a Utilização de índice
1 linha em conjunto, 1 de aviso (0.00 sec)
|
Como visto no exemplo, antes da adição de índice para a tabela, o MySQL usa externa de classificação para fazer GRUPO. For the example query I have forced the plan by using sql_big_result (as MySQL will not choose this plan for the dataset we have). Mas MySQL usaria este plano para agrupar na ausência de um índice para obter linhas ordenadas e usando uma tabela temporária torna-se caro por causa de um grande número de grupos. Uma vez que o índice é adicionado, ele recorre a usar o índice para fazer o grupo por.
mas ter linhas ordenadas antes do agrupamento não é uma necessidade. Optimizer pode decidir fazer uso de uma tabela temporária para fazê-lo. Cada linha nesta tabela seria uma linha agrupada e com cada linha recebida, a linha correspondente a esse grupo na tabela é atualizada. Não é necessário separar aqui. No entanto, como grupo em MySQL era esperado para ordenar, ele foi forçado a classificar as linhas agrupadas mesmo neste 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 identificação;
Query OK, 0 rows affected (0.01 sec)
Registros: 0 Duplicates: 0 Warnings: 0
mysql-5.7> explain SELECT id, SOMA(cnt) a PARTIR de t GROUP BY id \G
*************************** 1. linha ***************************
id: 1
select_type: SIMPLES
tabela: t
partições: NULL
tipo: ALL
possible_keys: NULL
chave: NULL
key_len: NULL
ref: NULL
linhas: 12
filtrado: 100.00
Extra: Uso temporário; Using filesort
1 linha em conjunto, 1 de aviso (0.00 sec)
|
a consulta De exemplo, podemos ver que, embora a tabela temporária é usada, O MySQL ainda não externos, classificação. Os usuários teriam que especificar explicitamente a ordem por NULL para deixar o MYSQL saber que grupo por necessidade não tem que Ordenar. Assim, uma sintaxe não-padrão (Ordem Por NULL) era necessária para neutralizar o efeito de outra extensão não-padrão (grupo por ordenação). É muito mais limpo agora que eliminámos essa confusão.
remoção da ordenação implícita para o grupo por
algum tempo atrás eu estava tentando corrigir o bug 71804. O repórter esperava MySQL para não fazer o arquivo desnecessário-sort que estava fazendo para o grupo por. Tentar fazer um patch para o bug nos fez perceber que otimizar esta situação em particular não é muito direto para a frente por causa do suporte para a ordenação implícita e explícita desse grupo pelo provided. Então nós concluímos que antes que essa otimização pudesse ser feita, nós deveríamos recontratar o código relacionado com a ordenação para grupo por.
o primeiro passo para fazer isso foi remover a ordenação implícita para o grupo por. Como mencionado no manual do Usuário aqui, foi decidido remover o suporte para ele algum tempo atrás . Ele foi feito como parte da característica do Índice descendente em 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, SOMA(cnt) a PARTIR de t GROUP BY id;
+——+———-+
| id | SOMA(cnt) |
+——+———-+
| 4 | 6 |
| 3 | 12 |
| 1 | 13 |
| 2 | 9 |
+——+———-+
4 linhas no set (0.00 sec)
mysql> explain SELECT id, SOMA(cnt) a PARTIR de t GROUP BY id \G
*************************** 1. linha ***************************
id: 1
select_type: SIMPLES
tabela: t
partições: NULL
tipo: ALL
possible_keys: NULL
chave: NULL
key_len: NULL
ref: NULL
linhas: 12
filtrado: 100.00
Extra: Usando temporário
1 linha em conjunto, 1 de aviso (0.00 sec)
|
Como visto no exemplo acima, a classificação não é realizada para a consulta. Como resultado, linhas agrupadas não são ordenadas no resultado final. Se os usuários precisam de linhas ordenadas, eles devem especificar a ordem na consulta.
no MySQL 5.7 e abaixo das versões, os usuários encontram o seguinte aviso no manual.
“GROUP BY
implicitamente o tipo por padrão (que é, na ausência de ASC
ou DESC
designadores de GROUP BY
colunas). No entanto, depender implícito GROUP BY
classificação (isto é, a classificação na ausência de ASC
ou DESC
designadores) ou explícita de classificação para a GROUP BY
(que é, usando explícita ASC
ou DESC
designadores de GROUP BY
colunas) é obsoleto. Para produzir uma dada ordem de ordenação, forneça uma cláusula ORDER BY
. “
remoção de ordenação explícita para o grupo por
Quando se tratava de remoção de ordenação explícita, era um pouco mais complicado fazê-lo. Nós não poderíamos removê-lo a menos que o MySQL suportasse a ordem com ROLLUP. ROLLUP com ordem BY Não foi permitido no MySQL 5.7 e versões anteriores. Assim, como alternativa, os usuários usariam grupo por ASC / DESC para obter dados ordenados com ROLLUP (embora a ordenação fosse muito restritiva com linhas super agregadas sempre colocadas após as linhas usadas para computá-las no caso de ASC e vice-versa para DESC). Tivemos de levantar esta limitação antes de removermos o apoio à classificação explícita do grupo por.
MySQL agora permite a ordem com ROLLUP. Expliquei pormenorizadamente como utilizar esta melhoria. Como explicado no mesmo blog, se os usuários querem a mesma ordem de ordenação de NULLs como a do MySQL 5.7 para ROLLUP, eles devem usar a função de agrupamento() para reescrever a consulta de uma forma simples.
assim, em suma, temos feito as seguintes coisas como precursores para remover a ordenação explícita para o grupo por.1. Adicionar função de agrupamento ()
2. Remoção da Classificação implícita para o grupo por
3. Permitindo a ordem com ROLLUP
e finalmente removemos a ordenação explícita para o grupo em MySQL 8.0.13.pedimos a opinião das comunidades há algum tempo. Concluímos que os usuários que estavam cientes desta extensão não-padrão que o MySQL forneceu não se importavam que ela desaparecesse.
conclusão
embora ainda tenhamos algum trabalho a fazer antes de corrigir o bug 71804, estamos felizes por termos feito isso. Por favor, informe-nos dos seus pensamentos. Obrigado por usar MySQL!