合并联接是唯一产生排序的联接算法 。
注:这个简化的合并联接不区分内表或外表;两个表扮演同样的角色 。但是真实的实现方式是不同的,比如当处理重复值时 。
1.(可选)排序联接运算:两个输入源都按照联接关键字排序 。
2.合并联接运算:排序后的输入源合并到一起 。
排序
我们已经谈到过合并排序,在这里合并排序是个很好的算法(但是并非最好的,如果内存足够用的话,还是哈希联接更好) 。
然而有时数据集已经排序了,比如:
- 如果表内部就是有序的,比如联接条件里一个索引组织表 【译者注: index-organized table 】
- 如果关系是联接条件里的一个索引
- 如果联接应用在一个查询中已经排序的中间结果
文章插图
这部分与我们研究过的合并排序中的合并运算非常相似 。不过这一次呢,我们不是从两个关系里挑选所有元素,而是只挑选相同的元素 。道理如下:
- 1) 在两个关系中,比较当前元素(当前=头一次出现的第一个)
- 2) 如果相同,就把两个元素都放入结果,再比较两个关系里的下一个元素
- 3) 如果不同,就去带有最小元素的关系里找下一个元素(因为下一个元素可能会匹配)
- 4) 重复 1、2、3步骤直到其中一个关系的最后一个元素 。
该算法是个简化版,因为它没有处理两个序列中相同数据出现多次的情况(即多重匹配) 。真实版本『仅仅』针对本例就更加复杂,所以我才选择简化版 。
如果两个关系都已经排序,时间复杂度是 O(N+M)
如果两个关系需要排序,时间复杂度是对两个关系排序的成本:O(N*Log(N) + M*Log(M))
对于计算机极客,我给出下面这个可能的算法来处理多重匹配(注:对于这个算法我不保证100%正确):
C
文章插图
哪个算法最好?
如果有最好的,就没必要弄那么多种类型了 。这个问题很难,因为很多因素都要考虑,比如:
- 空闲内存:没有足够的内存的话就跟强大的哈希联接拜拜吧(至少是完全内存中哈希联接) 。
- 两个数据集的大小 。比如,如果一个大表联接一个很小的表,那么嵌套循环联接就比哈希联接快,因为后者有创建哈希的高昂成本;如果两个表都非常大,那么嵌套循环联接CPU成本就很高昂 。
- 是否有索引:有两个 B+树索引的话,聪明的选择似乎是合并联接 。
- 结果是否需要排序:即使你用到的是未排序的数据集,你也可能想用成本较高的合并联接(带排序的),因为最终得到排序的结果后,你可以把它和另一个合并联接串起来(或者也许因为查询用 ORDER BY/GROUP BY/DISTINCT 等操作符隐式或显式地要求一个排序结果) 。
- 关系是否已经排序:这时候合并联接是最好的候选项 。
- 联接的类型:是等值联接(比如 tableA.col1 = tableB.col2 )? 还是内联接?外联接?笛卡尔乘积?或者自联接?有些联接在特定环境下是无法工作的 。
- 数据的分布:如果联接条件的数据是倾斜的(比如根据姓氏来联接人,但是很多人同姓),用哈希联接将是个灾难,原因是哈希函数将产生分布极不均匀的哈希桶 。
- 如果你希望联接操作使用多线程或多进程 。
简化的例子
我们已经研究了 3 种类型的联接操作 。
现在,比如说我们要联接 5 个表,来获得一个人的全部信息 。一个人可以有:
- 多个手机号(MOBILES)
- 多个邮箱(MAILS)
- 多个地址(ADRESSES)
- 多个银行账号(BANK_ACCOUNTS)
MySQL
文章插图
作为一个查询优化器,我必须找到处理数据最好的方法 。但有 2 个问题:
- 每个联接使用那种类型?
我有 3 种可选(哈希、合并、嵌套),同时可能用到 0, 1 或 2 个索引(不必说还有多种类型的索引) 。
推荐阅读
- 如何挑选黑豆
- 不是夫妻合租房犯罪吗
- 医保断缴三个月余额清零?员工可自愿放弃社保?这些谣言别再信了
- 手串颗数大有讲究,千万别戴错了
- “禁止长时间停车”,到底指的是几分钟?交警:最后再说一遍
- 太热了!别再披头散发了,这4款发型够美够清凉
- 没钱还信用卡有什么解决办法
- 信用卡过期卡怎么处理
- 信用卡逾期被注销怎么还钱
- 如何找回抖音私聊记录