mysql分组后组内求差_求教,Mysqlgroupby后对分组数据的
处理问题
mysql> select k, count(*) c from tbl group by k order by k limit 5;
+---+---+
| k | c |
+---+---+
| 2 | 3 |
| 4 | 1 |
| 5 | 2 |
| 8 | 1 |
| 9 | 1 |
+---+---+
5 rows in set (0.00 sec)
mysql> explain select k, count(*) c from tbl group by k order by k limit 5 G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
partitions: NULL
type: index
possible_keys: k
key: k
key_len: 4
ref: NULL
rows: 5
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
在这种情况下,我们在 GROUP BY 的列上有⼀个索引。这样,我们可以逐组扫描数据并动态执⾏ GROUP BY(低成本)。当我们使⽤LIMIT 限制我们检索的组的数量或使⽤“覆盖索引”时,特别有效,因为顺序索引扫描是⼀种⾮常快速的操作。
如果您有少量组,并且没有覆盖索引,索引顺序扫描可能会导致⼤量 IO。所以这可能不是最优化的计划。
2、MySQL 中的外部排序 GROUP BY
mysql> explain select SQL_BIG_RESULT g, count(*) c from tbl group by g limit 5 G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 998490
filtered: 100.00
Extra: Using filesort
1 row in set, 1 warning (0.00 sec)
mysql> select SQL_BIG_RESULT g, count(*) c from tbl group by g limit 5;
+---+---+
| g | c |
+---+---+
| 0 | 1 |
| 1 | 2 |
| 4 | 1 |
| 5 | 1 |
| 6 | 2 |
+---+---+
5 rows in set (0.88 sec)
如果我们没有允许我们按组顺序扫描数据的索引,我们可以通过外部排序(在 MySQL 中也称为“filesort”)来获取数据。你可能会注意到我在这⾥使⽤ SQL_BIG_RESULT 提⽰来获得这个计划。没有它,MySQL 在这种情况下不会选择这个计划。
⼀般来说,MySQL 只有在我们拥有⼤量组时才更喜欢使⽤这个计划,因为在这种情况下,排序⽐拥有临时表更有效(我们将在下⾯讨论)。
3、MySQL中 的临时表 GROUP BY
mysql> explain select  g, sum(g) s from tbl group by g limit 5 G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
partitions: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 998490
filtered: 100.00
Extra: Using temporary
1 row in set, 1 warning (0.00 sec)
mysql> select  g, sum(g) s from tbl group by g order by null limit 5;
+---+------+
| g | s    |
+---+------+
| 0 |    0 |
| 1 |    2 |
| 4 |    4 |
| 5 |    5 |
| 6 |  12 |
+---+------+
5 rows in set (7.75 sec)
在这种情况下,MySQL 也会进⾏全表扫描。但它不是运⾏额外的排序传递,⽽是创建⼀个临时表。此临时表每组包含⼀⾏,并且对于每个传⼊⾏,将更新相应组的值。很多更新!虽然这在内存中可能是合理的,但如果结果表太⼤以⾄于更新将导致⼤量磁盘 IO,则会变得⾮常昂贵。在这种情况下,外部分拣计划通常更好。请注意,虽然 MySQL 默认选择此计划⽤于此⽤例,但如果我们不提供任何提⽰,它⼏乎⽐我们使⽤ SQL_BIG_RESULT 提⽰的计划慢 10 倍 。您可能会注意到我在此查询中添加了“ ORDER BY NULL ”。这是为了向您展
⽰“清理”临时表的唯⼀计划。没有它,我们得到这个计划:    mysql> explain select  g, sum(g) s from tbl group by g limit 5 G
*************************** 1. row ***************************              id: 1    select_type: SIMPLE          table: tbl      partitions: NULL          type: ALL    possible_keys: NULL            key: NULL        key_len: NULL            ref: NULL            rows: 998490
filtered: 100.00          Extra: Using temporary; Using filesort    1 row in set, 1 warning (0.00 sec)
在其中,我们获得了 temporary 和 filesort “两最糟糕的”提⽰。MySQL 5.7 总是返回按组顺序排序的 G
ROUP BY 结果,即使查询不需要它(这可能需要昂贵的额外排序传递)。ORDER BY NULL 表⽰应⽤程序不需要这个。您应该注意,在某些情况下 - 例如使⽤聚合函数访问不同表中的列的 JOIN 查询 - 使⽤ GROUP BY 的临时表可能是唯⼀的选择。
如果要强制 MySQL 使⽤为 GROUP BY 执⾏临时表的计划,可以使⽤ SQL_SMALL_RESULT 提⽰。
4、MySQL 中的索引基于跳过扫描的 GROUP BY前三个 GROUP BY 执⾏⽅法适⽤于所有聚合函数。然⽽,其中⼀些⼈有第四种⽅法。
mysql> explain select k,max(id) from tbl group by k G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
partitions: NULL
type: range
possible_keys: k
key: k
groupby分组key_len: 4
ref: NULL
rows: 2
filtered: 100.00
Extra: Using index for group-by
1 row in set, 1 warning (0.00 sec)
mysql> select k,max(id) from tbl group by k;
+---+---------+
| k | max(id) |
+---+---------+
| 0 | 2340920 |
| 1 | 2340916 |
| 2 | 2340932 |
| 3 | 2340928 |
| 4 | 2340924 |
+---+---------+
5 rows in set (0.00 sec)
此⽅法仅适⽤于⾮常特殊的聚合函数:MIN() 和 MAX()。这些并不需要遍历组中的所有⾏来计算值。他们可以直接跳转到组中的最⼩或最⼤组值(如果有这样的索引)。如果索引仅建⽴在 (K) 列上,如何到每个组的 MAX(ID) 值?这是⼀个 InnoDB 表。记住 InnoDB 表有效地将 PRIMARY KEY 附加到所有索引。(K) 变为 (K,ID),允许我们对此查询使⽤ Skip-Scan 优化。仅当每个组有⼤量⾏时才会启⽤此优
化。否则,MySQL 更倾向于使⽤更传统的⽅法来执⾏此查询(如⽅法#1中详述的索引有序 GROUP BY)。虽然我们使⽤ MIN() / MAX() 聚合函数,但其他优化也适⽤于它们。例如,如果您有⼀个没有 GROUP BY 的聚合函数(实际上所有表都有⼀个组),MySQL 在统计分析阶段从索引中获取这些值,并避免在执⾏阶段完全读取表:    mysql> explain select max(k) from tbl G    *************************** 1. row
***************************              id: 1    select_type: SIMPLE          table: NULL      partitions: NULL            type: NULL
possible_keys: NULL            key: NULL        key_len: NULL            ref: NULL            rows: NULL        filtered: NULL          Extra: Select tables optimized away    1 row in set, 1 warning (0.00 sec)
过滤和分组
我们已经研究了 MySQL 执⾏ GROUP BY 的四种⽅式。为简单起见,我在整个表上使⽤了 GROUP BY,没有应⽤过滤。当您有 WHERE ⼦句时,相同的概念适⽤:    mysql> explain select  g, sum(g) s from tbl where k>4 group by g order by NULL limit 5 G
*************************** 1. row ***************************              id: 1    select_type: SIMPLE          tab
le: tbl      partitions: NULL          type: range    possible_keys: k            key: k        key_len: 4            ref: NULL            rows: 1        filtered: 100.00          Extra: Using index condition; Using temporary    1 row in set, 1 warning (0.00 sec)
对于这种情况,我们使⽤K列上的范围进⾏数据过滤/查,并在有临时表时执⾏ GROUP BY。在某些情况下,⽅法不会发⽣冲突。但是,在其他情况下,我们必须选择使⽤ GROUP BY 的⼀个索引或其他索引进⾏过滤:
mysql> alter table tbl add key(g);
Query OK, 0 rows affected (4.17 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> explain select  g, sum(g) s from tbl where k>1 group by g limit 5 G *************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
partitions: NULL
type: index
possible_keys: k,g
key: g
key_len: 4
ref: NULL
rows: 16
filtered: 50.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
mysql> explain select  g, sum(g) s from tbl where k>4 group by g limit 5 G *************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tbl
partitions: NULL
type: range
possible_keys: k,g
key: k
key_len: 4
ref: NULL
rows: 1
filtered: 100.00
Extra: Using index condition; Using temporary; Using filesort
1 row in set, 1 warning (0.00 sec)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。