二元操作的一趟算法
专栏内容:
- 手写数据库toadb
本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。
本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学习。
开源贡献:
- toadb开源库
个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
文章目录
- 二元操作的一趟算法
- 前言
- 概述
- 并集
- 包的并集
- 集合的并集
- 交集
- 包的交集
- 集合的交集
- 差集
- 包的差集
- 集合的差集
- 积
- 连接
- 总结
- 结尾
前言
随着信息技术的飞速发展,数据已经渗透到各个领域,成为现代社会最重要的资产之一。在这个大数据时代,数据库理论在数据管理、存储和处理中发挥着至关重要的作用。然而,很多读者可能对数据库理论感到困惑,不知道如何选择合适的数据库,如何设计有效的数据库结构,以及如何处理和管理大量的数据。因此,本专栏旨在为读者提供一套全面、深入的数据库理论指南,帮助他们更好地理解和应用数据库技术。
数据库理论是研究如何有效地管理、存储和检索数据的学科。在现代信息化社会中,数据量呈指数级增长,如何高效地处理和管理这些数据成为一个重要的问题。同时,随着云计算、物联网、大数据等新兴技术的不断发展,数据库理论的重要性日益凸显。
因此,本专栏的分享希望可以提高大家对数据库理论的认识和理解,对于感兴趣的朋友带来帮助。
概述
前一篇博文分享了一趟算法的机制和在一元操作中的应用,代价评估。本文将分享一趟算法在二元操作中的应用,代价估算,以及存在的优化点。
在关系代数中,涉及到的二元操作主要是集合操作,如并集,交集,差集,积和连接。
同样对于适用一趟算法的限制,还是可分配的缓冲区大小M,如果表足够小,或者结果集能装入缓冲区,那么就可以使用一趟算法,否则需要二趟或更多趟,这在后续博文中会分享,敬请期待。
并集
这里的并集,要区分对集合的操作还是对于包的操作。
包的并集
如果是包的并集,那可以通过一种非常简单的一趟算法计算出来,我们先通过迭代器获取到表R的每一个元组,输出到结果,然后再获取表S的每一个元组,输出到结果。
集合的并集
而对于集合的并集操作,我们需要先将较小的表S先读取加载到缓冲区中,并使用一种便于查找的数据结构,如查找树或者hash表等;将表S的所有元组输出到结果;
然后将较大表R使用迭代器,每次获取一个元组,检查在表S中是否存在,如不存在,则将此元组输出结果,如果存在的话,则忽略。 直到表R所有元组处理完成。
对于包和集合的操作,在此过程中,只需要一个缓冲区块即可,而磁盘IO的数量为两表文件所占数据块的和。
交集
这里的交集,也是要区分对集合的操作还是对于包的操作。
包的交集
包的交集处理时,也是将较小的表S先加载到缓冲区中的查找结构中,但是对于表中的重复元组,不再重复保留多个副本,而是采用元组与和它的数量计数器。
然后将第二张表R使用迭代器,每次获取一个元组,检查在表S中是否存在。
- 如不存在,则忽略;
- 如果存在的话,将表S中对应的数据的计数减1;此时计数已经为负值,那么就删除表S中的对应元组;
直到表R的所有元组处理完成,最后剩余的元组中,计数器结果为0的就是结果,输出,也就是两个包中元组及元组副本数相同的部分。
集合的交集
而对于集合的交集操作,我们需要先将较小的表S先读取加载到缓冲区中,并使用一种便于查找的数据结构,如查找树或者hash表等;将表S的所有元组存放在查找数据结构中;
然后将较大表R使用迭代器,每次获取一个元组,检查在表S中是否存在,如不存在,则忽略;如果存在的话,则将此元组输出结果,直到表R所有元组处理完成。
对于包和集合的操作,在此过程中,只需要的缓冲区块是较小表的数据块再加1个,当然对于包交时,副本越多需要的缓冲区越少,而磁盘IO的数量为两表文件所占数据块的和。
差集
这里的差集,也是要区分对集合的操作还是对于包的操作。
差集是一种不能交换的操作,表R-表S的差,和表S-表R的差,是两种差集。
包的差集
包的差集处理时,也是将第一个表S先加载到缓冲区中的查找结构中,但是对于表中的重复元组,不再重复保留多个副本,而是采用元组与和它的数量计数器。
然后将第二张表R使用迭代器,每次获取一个元组,检查在表S中是否存在。
- 如不存在,则输出到结果;
- 如果存在的话,此时计数已经为0,那么就输出结果;如果计数大于0,将表S中对应的数据的计数减1;
直到表R的所有元组处理完成。
集合的差集
而对于集合的差集操作,我们需要先将第一个表S先读取加载到缓冲区中,并使用一种便于查找的数据结构,如查找树或者hash表等;将表S的所有元组存放在查找数据结构中;
然后将第二张表R使用迭代器,每次获取一个元组,检查在表S中是否存在。
- 如不存在,则忽略;
- 如果存在的话,将表S中对应的数据删除;
直到表R的所有元组处理完成,最后剩余的元组就是结果,输出。
对于包和集合的操作,在此过程中,只需要的缓冲区块是较小表的数据块再加1个,而磁盘IO的数量为两表文件所占数据块的和。
积
积操作时,将较小表S加载到缓冲区中,不需要查找结构;然后从迭代器中获取第二张表R的每一条元组,依次与表S中的每一个元组进行积的操作,并输出结果。
这个过程中,缓冲区也是需要表S的数据块数量+1个,磁盘IO是两表的数据块的和;而对于积操作时,需要元组数n平方的计算时间。
连接
这里我们以自然连接为例进行分享。
当表R(X,Y)与表S(Y,Z)进行连接时,连接属性为Y,是两表的公共属性。
先是将较小表S加载到缓中区中,建立属性Y上的查找表;
然后将第二张表R通过迭代器,获取每一条元组,检查在查找表中是否存在:
- 如不存在,则忽略;
- 如果存在的话,将表S中对应的元组与表R的元组组成新元组输出;
直到表R的所有元组处理完成。
等值连接与自然连接的执行方式一样,当然我们需要考虑两个连接属性的名字不同的情况;
总结
经过上面对一趟算法在二元操作中应用的分享后,可以看到这一算法读取操作对象需要使用两表的数据块和的次数磁盘IO;需要的缓冲区数量近似于一张表的数据块数量;而在连接和积时,会额外考虑计算代价。
结尾
最后分享一段helloworld的代码
在GCC中,正确的析构函数应该使用 __attribute__((destructor))
。
__attribute__((destructor)) void aftermain() {
printf("Hello, World!\n");
}
int main() {
return 0;
}
在这个示例中,aftermain
函数被设定为析构函数,这意味着当程序结束时,它将自动执行。所以,无论你的 main
函数是否返回 0
,aftermain
函数都会被调用并输出 “Hello, World!”。
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。