• 30 “select distinct(field1)“ 的实现


    前言

    在日常工作生活中, 我们应该经常会使用到 select distinct(field) from table; 等功能 

    来实现 获取符合条件的 field 的去重之后的记录信息

    然后 我们这里来看一下 具体的实现

     

    测试数据表如下 

    1. CREATE TABLE `tz_test` (
    2. `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    3. `field1` varchar(12) DEFAULT NULL,
    4. `field2` varchar(16) DEFAULT NULL,
    5. PRIMARY KEY (`id`) USING BTREE,
    6. KEY `field1` (`field1`) USING BTREE
    7. ) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8

     

    测试数据列表如下 

    5479249a3bdd432cbde14f6e68dc5bd8.png

     

     

    distinct 的实现

    这里来看一下 “select distinct(field1) from tz_test;”

    接下来 分为三种情况, 分别 distinct 三种不同的字段 

     

     

    假设目标字段是主键索引的情况

    查询主键索引, 然后迭代数据返回 比如这里是 id 为 1 的记录 

    1a8d9b5c0ed74c36a56a2bd2e9c3ceb3.png

     

    如下为 id 为10 的记录 

    cbf403a4e75147668cb0bdab22289989.png

     

     

    假设目标字段有索引的情况

    执行 explain 情况如下 

    f27a8e8d40204dae98cbbb25cd52bc1a.png

     

    我们这里 field1 有索引, 我们来看一下 实现 的情况, 这个走的是 field1 的索引树 

    这里遍历的是 field1 索引树, 会依次遍历 

    1. field1->1
    2. field2->2
    3. field3->3
    4. field4->4
    5. field4->7
    6. field5->5
    7. field5->6
    8. field5->8
    9. field5->9
    10. field5->10

    9956304bff3845b09b71baa7ffbdd434.png

     

    对于 “field1->1, field2->2, field3->3, field4->4” 会走 “test_if_item_cache_changed” 对应的 if 的 block, 其中会输出字段 field1 的值 

    b677bda8248043ebb5708605c9462a0d.png

     

    对于 “field4->7” 不会走 “test_if_item_cache_changed” 对应的 if 的 block, 就不会输出字段信息到 客户端

    join->group_fields 中存放的是 field1 上一次迭代的数据的值 

    7b4bd37ed6d84eb583ddda8c03726efb.png

     

    对于 “field5->5”, 会走 “test_if_item_cache_changed” 对应的 if 的 block, 其中会输出字段 field1 的值 

    788a884e3c1a44b3af88d2a56896577a.png

     

    对于 “field5->6, field5->8, field5->9, field5->10” 不会走 “test_if_item_cache_changed” 对应的 if 的 block, 就不会输出字段信息到 客户端

    34384047791c4ba5b5437a9470ca2f5b.png

     

    假设目标字段无索引的情况 

    为了更好的调试, 更新 field2 的字段数据如下 

    ccf13f7c78f140d8bd0463c9acab6c33.png

     

    执行 explain 情况如下 

    4d623a31feef4fee963b01f84f2cff75.png

    这个的实现是基于 临时表来实现的

    数据从真实业务表, 抽取到临时表的处理如下 

    外层的 sub_select 是遍历 tz_test 表的常规流程, 然后这里的处理是 拷贝需要的字段, 然后使用 table->file->ha_write_row 入库临时表 

    然后使用同样的步骤拷贝了 ”field2”, “field3” , “field4” , “field5” 到临时表

    a6215dc4eda8493cb3bcb8c0bb0e0204.png

     

    然后临时表的 table->record[0] 信息变化如下, 可以看到将 field2 的字段数据拷贝进去了 

    临时表仅仅只有一个字段 field2

    1f3b136ab50449378d166dfa396e2392.png

     

    临时表结构如下, 一个字段, 然后字段名称为 field2

    bf85872b998440f6bf00eeab30aaa62f.png

    然后向临时表写出数据的处理是在这里

    临时表是基于 ha_heap, 将数据存放在内存中的, 和之前提到过的 union 的处理类似 

    ca94ffa2147b4fcf9822564b6f3c22ed.png

     

    接下来的 “field4” , “field5” , “field5” , “field5” 由于存在唯一约束, 因此 接下来这几个记录的 field2 没有入库 

    b833b3ad55504f54b965a14abb4a20cf.png

    然后是迭代临时表的数据, 响应给客户端 

    如下图 join_init_read_record + qep_tab->read_record.read_record 为迭代临时表中的数据

    evaluate_join_record 为输出结果信息到客户端 

    17dc1f1563e34d4cb62883d9d44ee636.png

     

    迭代临时表中所有的记录, 响应回去

    依次为 ”field1”, ”field2”, “field3” , “field4” , “field5”

    1cb1b82da46b49f6b39822737526ed43.png

     

    创建临时表, 以及定义 keyDefinition 的地方 

    405f874b8c2446a7830fad88bffc4f72.png

     

     完

     

     

     

  • 相关阅读:
    基于STM32的智能鱼缸设计
    Global Mapper 导出图层功能的妙用(重采样、设置文件类型、切片、按掩膜提取or裁剪……)
    云原生之深入解析Kubernetes Pod的网络状态监控
    推荐3款小众软件,可以满足一些奇怪的需求
    vue2实现可拖拽甘特图(结合element-ui的gantt图)
    Git远程分支操作
    Desthiobiotin衍生物Desthiobiotin-PEG4-Amine/Alkyne/Azide/DBCO
    旅游景区地图导览系统,传统导览智慧新升级
    13.状态模式
    产品研发团队协作神器!10款提效工具大盘点!
  • 原文地址:https://blog.csdn.net/u011039332/article/details/131216257