最近,我面临一个问题,mysql如何实现松散的索引扫描?
例如:
测试表结构是:
CREATE TABLE test
(
id
int(11) NOT NULL default '0',
v1
int(10) unsigned NOT NULL default '0',
v2
int(10) unsigned NOT NULL default '0',
v3
int(10) unsigned NOT NULL default '0',
PRIMARY KEY (id
),
KEY v1_v2_v3
(v1
,v2
,v3
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
select * from test;
+----+----+-----+----+
| id | v1 | v2 | v3 |
+----+----+-----+----+
| 1 | 1 | 0 | 1 |
| 2 | 3 | 1 | 2 |
| 10 | 4 | 10 | 10 |
| 0 | 4 | 100 | 0 |
| 3 | 4 | 100 | 3 |
| 5 | 5 | 9 | 5 |
| 8 | 7 | 3 | 8 |
| 7 | 7 | 4 | 7 |
| 30 | 8 | 15 | 30 |
+----+----+-----+----+
现在让我们看看两个sql: 第一个:
test
我知道id
意味着MySQL使用松散的索引扫描来查询sql.But为什么解释输出列v1
是3?我想知道MySQL如何只扫描三行并获得查询结果。
第二个:
v2
上述两个sql之间的唯一区别在于v3
,一个是id
,另一个是v1_v2_v3
。但为什么v1
使用松散的索引扫描,v2
不要使用松散的索引扫描?我不明白GROUP BY Optimization说:
选择列表中使用的唯一聚合函数(如果有)是MIN()和MAX(),并且它们都引用同一列。该列必须位于索引中,并且必须紧跟GROUP BY中的列。
为什么列必须紧跟GROUP BY中的列?
我在网上搜索了很长时间。但没用。请帮助或尝试提供一些如何实现这一点的想法。 谢谢!
答案 0 :(得分:0)
评论太长了。
基本上,在询问“为什么优化器会以某种方式运行”时,答案是因为设计人员以这种方式实现了它。如果你想知道“为什么”,你就得问问他们。 。 。对于通用论坛来说,这不是一个合适的问题。
但是,我想指出一些事情。如果您认为max(v2)
是一个错误,那么您可以在bugs.mysql.com上报告它。我不认为这是一个错误有两个原因:
group by
中的键)。group by
密钥上使用聚合函数也是非理性的。它是有效的SQL,但它只是冗长而且不必要。这些构造在数据库实现者的优先级列表中排在最前面。最后,在创建查询计划时,MySQL并没有真正使用统计数据(非常好?)。但是,在大多数数据库中,验证9行(适合单个数据页)上的查询计划通常会导致执行全表扫描和“低效”算法的查询计划。例如,冒泡排序等算法在大量行上效率很低,但它可以是(非常)少量行上最有效的排序算法。
答案 1 :(得分:0)
有没有理由在查询中使用max(v2)?即使您不使用max()函数,结果也是一样的。如果您将查询更改为"请从test中选择v2,其中v1> 3组按v1,v2和#34;,它将通过松散索引扫描方法完成。
以下是列必须紧跟GROUP BY中列的原因。
v1 v2 v3
1 1 1
1 1 2
1 1 10
1 2 1
1 2 2
1 2 8
在这种情况下,通过v1,v2从t1组中选择max(v3)以执行松散索引扫描。这样做如下图所示。
v1 v2 v3
1 1 1
1 1 2
1 1 10 ------------------> 10 return
1 2 1
1 2 2
1 2 8 ------------------> 8 return
但是,如果您通过v1从t1组执行select max(v3),则无法进行松散索引扫描。因为您必须访问所有键才能找到最大值(= 10)。
v1 v2 v3
1 1 1 ------------------> (x)
1 1 2 ------------------> (x)
1 1 10 ------------------> 10 return
1 2 1 ------------------> (x)
1 2 2 ------------------> (x)
1 2 8 ------------------> (x)
请注意,您可以使用以下命令查看使用松散索引扫描(或紧密索引扫描)访问的记录数。
flush status;
select max(v3) from t1 group by v1,v2; -- perform loose index scan
show session status like 'Handler_read_key%';
flush status;
select max(v3) from t1 group by v1; -- perform tight index scan
show session status like 'Handler_read_key%';