在日常工作生活中, 我们应该经常会使用到 select distinct(field) from table; 等功能
来实现 获取符合条件的 field 的去重之后的记录信息
然后 我们这里来看一下 具体的实现
测试数据表如下
CREATE TABLE `tz_test` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`field1` varchar(12) DEFAULT NULL,
`field2` varchar(16) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `field1` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8
测试数据列表如下
这里来看一下 “select distinct(field1) from tz_test;”
接下来 分为三种情况, 分别 distinct 三种不同的字段
查询主键索引, 然后迭代数据返回 比如这里是 id 为 1 的记录
如下为 id 为10 的记录
执行 explain 情况如下
我们这里 field1 有索引, 我们来看一下 实现 的情况, 这个走的是 field1 的索引树
这里遍历的是 field1 索引树, 会依次遍历
field1->1
field2->2
field3->3
field4->4
field4->7
field5->5
field5->6
field5->8
field5->9
field5->10
对于 “field1->1, field2->2, field3->3, field4->4” 会走 “test_if_item_cache_changed” 对应的 if 的 block, 其中会输出字段 field1 的值
对于 “field4->7” 不会走 “test_if_item_cache_changed” 对应的 if 的 block, 就不会输出字段信息到 客户端
join->group_fields 中存放的是 field1 上一次迭代的数据的值
对于 “field5->5”, 会走 “test_if_item_cache_changed” 对应的 if 的 block, 其中会输出字段 field1 的值
对于 “field5->6, field5->8, field5->9, field5->10” 不会走 “test_if_item_cache_changed” 对应的 if 的 block, 就不会输出字段信息到 客户端
为了更好的调试, 更新 field2 的字段数据如下
执行 explain 情况如下
这个的实现是基于 临时表来实现的
数据从真实业务表, 抽取到临时表的处理如下
外层的 sub_select 是遍历 tz_test 表的常规流程, 然后这里的处理是 拷贝需要的字段, 然后使用 table->file->ha_write_row 入库临时表
然后使用同样的步骤拷贝了 ”field2”, “field3” , “field4” , “field5” 到临时表
然后临时表的 table->record[0] 信息变化如下, 可以看到将 field2 的字段数据拷贝进去了
临时表仅仅只有一个字段 field2
临时表结构如下, 一个字段, 然后字段名称为 field2
然后向临时表写出数据的处理是在这里
临时表是基于 ha_heap, 将数据存放在内存中的, 和之前提到过的 union 的处理类似
接下来的 “field4” , “field5” , “field5” , “field5” 由于存在唯一约束, 因此 接下来这几个记录的 field2 没有入库
然后是迭代临时表的数据, 响应给客户端
如下图 join_init_read_record + qep_tab->read_record.read_record 为迭代临时表中的数据
evaluate_join_record 为输出结果信息到客户端
迭代临时表中所有的记录, 响应回去
依次为 ”field1”, ”field2”, “field3” , “field4” , “field5”
创建临时表, 以及定义 keyDefinition 的地方
完