上一篇里面我们实现了单表查询和top N查询,这一篇我们来讲述如何实现多表连接和group by分组。

一、多表连接

多表连接的时间是数据库一个非常耗时的操作,因为连接的时间复杂度是M*N(M,N是要连接的表的记录数),如果不对进行优化,连接的产生的临时表可能非常大,需要写入磁盘,分多趟进行处理。

1、双表等值join

我们看这样一个连接sql:

selectPS_AVAILQTY,PS_SUPPLYCOST,S_NAMEfromSUPPLIER,PARTSUPPwhere PS_SUPPKEY =S_SUPPKEYand PS_AVAILQTY > 2000and S_NATIONKEY = 1;

可以把这个sql理解为在SUPPLIER表的S_SUPPKEY属性和PARTSUPP表的PS_SUPPKEY属性上作等值连接,并塞选出满足PS_AVAILQTY > 2000和 S_NATIONKEY = 1的记录,输入满足条件记录的PS_AVAILQTY,PS_SUPPLYCOST,S_NAME属性。这样的理解对我们人来说是很明了的,但数据库不能照这样的方式执行,上面的PS_SUPPKEY其实是PARTSUPP的外键,两个表进行等值连接,得到的连接结果是很大的。所以我们应该先从单表查询条件入手,在单表查询过滤之后再进行等值连接,这样需要连接的记录数会少很多。

首先根据PS_AVAILQTY > 2000找出满足条件的PARTSUPP表的记录行号集A,然后根据S_NATIONKEY = 1找出SUPPLIER表找出相应的记录行号集B,在记录集A、B上进行等值连接,看图很简单:

依次扫描的时间复杂度为max(m,n),加上折半查找,总的时间复杂度为max(m,n)*(log(m1)+log(n1)),其中m1、n1表示where条件塞选出的记录数。

来看一下执行的结果:

Input SQL:selectPS_AVAILQTY,PS_SUPPLYCOST,S_NAMEfromSUPPLIER,PARTSUPPwhere PS_SUPPKEY =S_SUPPKEYand PS_AVAILQTY > 2000

and S_NATIONKEY = 1;

{'FROM': ['SUPPLIER', 'PARTSUPP'],'GROUP': None,'ORDER': None,'SELECT': [['PARTSUPP.PS_AVAILQTY', None, None],['PARTSUPP.PS_SUPPLYCOST', None, None],['SUPPLIER.S_NAME', None, None]],'WHERE': [['PARTSUPP.PS_AVAILQTY', '>', '2000'],['SUPPLIER.S_NATIONKEY', '=', '1'],['PARTSUPP.PS_SUPPKEY', '=', 'SUPPLIER.S_SUPPKEY']]}

Quering: PARTSUPP.PS_AVAILQTY> 2000Quering: SUPPLIER.S_NATIONKEY= 1Quering: PARTSUPP.PS_SUPPKEY=SUPPLIER.S_SUPPKEY

Output:

The result hava26322 rows, here is the fisrt 10rows:-------------------------------------------------

rows PARTSUPP.PS_AVAILQTY PARTSUPP.PS_SUPPLYCOST SUPPLIER.S_NAME-------------------------------------------------

1 8895 378.49 Supplier#000000003

2 4286 502.00 Supplier#000000003

3 6996 739.71 Supplier#000000003

4 4436 377.80 Supplier#000000003

5 6728 529.58 Supplier#000000003

6 8646 722.34 Supplier#000000003

7 9975 841.19 Supplier#000000003

8 5401 139.06 Supplier#000000003

9 6858 786.94 Supplier#000000003

10 8268 444.21 Supplier#000000003

-------------------------------------------------

Take 26.58 seconds.

从Quering后面的信息可以看到我们处理where子条件的顺序,先处理单表查询,再处理多表连接。

2、多表join

处理完双表join后,我们看一下怎么实现三个的join,示例sql:

selectPS_AVAILQTY,PS_SUPPLYCOST,S_NAMEfromSUPPLIER,PART,PARTSUPPwhere PS_PARTKEY =P_PARTKEYand PS_SUPPKEY =S_SUPPKEYand PS_AVAILQTY > 2000

and P_BRAND = 'Brand#12'

and S_NATIONKEY = 1;

这里进行三个表的连接,三个表连接得到的应该是三个表的记录合并的结果,那根据where条件选出的记录行号应当包含三列,每一列是一个表的行号:

三个表的连接事实上建立在两个表连接的基础上的,先进行两个表的连接后,得到两组行号表,再将这两组行号表合并:

主要代码如下:

1 sortJoin(joina,cloumi)#cloumi表示公共表在joina的列号

2 sortJoin(joinb,cloumj)#cloumj表示公共表在joinb的列号

3 i = j = 0#左右指针初试为0

4 while i < len(joina) and j

7 elif joina[i][cloumi] >joinb[j][cloumj]:8 j += 1

9 else:#相等,进行连接

10 lastj =j11 while j < len(joinb) and joina[i][cloumi] ==joinb[j][cloumj]:12 temp = joina[i] +joinb[j]13 temp.remove(joina[i][cloumi])#删掉重复的元素

14 mergeResult.append(temp)15 j += 1

16 j = lastj#右指针回滚

17 i += 1

我们分析一下这个算法的时间复杂度,首先要对两个表排序,复杂度为O(m1log(m1)),在扫描的过程中,右边指针会回溯,所以不再是O(max(m1,n1)),我们可以认为是k*O(m1*n1),这个系数k应该是很小的,因为一般右指针不会回溯太远,总的时间复杂度是O(m1log(m1))+k*O(m1*n1),应该是小于N方的复杂度。

看一下执行的结果:

Input SQL:

select PS_AVAILQTY,PS_SUPPLYCOST,S_NAMEfromSUPPLIER,PART,PARTSUPP

where PS_PARTKEY=P_PARTKEYand PS_SUPPKEY =S_SUPPKEYand PS_AVAILQTY > 2000

and P_BRAND = 'Brand#12'

and S_NATIONKEY = 1;

{'FROM': ['SUPPLIER', 'PART', 'PARTSUPP'],'GROUP': None,'ORDER': None,'SELECT': [['PARTSUPP.PS_AVAILQTY', None, None],

['PARTSUPP.PS_SUPPLYCOST', None, None],

['SUPPLIER.S_NAME', None, None]],'WHERE': [['PARTSUPP.PS_AVAILQTY', '>', '2000'],

['PART.P_BRAND', '=', 'Brand#12'],

['SUPPLIER.S_NATIONKEY', '=', '1'],

['PARTSUPP.PS_PARTKEY', '=', 'PART.P_PARTKEY'],

['PARTSUPP.PS_SUPPKEY', '=', 'SUPPLIER.S_SUPPKEY']]}

Quering: PARTSUPP.PS_AVAILQTY> 2000Quering: PART.P_BRAND= Brand#12

Quering: SUPPLIER.S_NATIONKEY = 1Quering: PARTSUPP.PS_PARTKEY=PART.P_PARTKEY

Quering: PARTSUPP.PS_SUPPKEY=SUPPLIER.S_SUPPKEY

Output:

The result hava1022 rows, here is the fisrt 10rows:-------------------------------------------------rows PARTSUPP.PS_AVAILQTY PARTSUPP.PS_SUPPLYCOST SUPPLIER.S_NAME-------------------------------------------------

1 4925 854.19 Supplier#000002515

2 4588 455.04 Supplier#000005202

3 8830 852.13 Supplier#000007814

4 8948 689.89 Supplier#000002821

5 3870 488.38 Supplier#000005059

6 6968 579.03 Supplier#000005660

7 9269 228.31 Supplier#000000950

8 8818 180.32 Supplier#000003453

9 9343 785.01 Supplier#000003495

10 3364 545.25 Supplier#000006030

-------------------------------------------------Take50.42 seconds.

这个查询的时间比Mysql快了很多,在mysql上运行这个查询需要10分钟(建立了索引),想想也是合理的,我们的设计已经大大简化了,完全不考虑表的修改,牺牲这么的实用性必然能提升在查询上的效率。

二、group by分组

在执行完where条件后,读取原始记录,然后可以按group by的属性分组,分组的属性可能有多条,比如这样一个查询:

select PS_AVAILQTY,PS_SUPPLYCOST,S_NAME,COUNT(*)fromSUPPLIER,PART,PARTSUPP

where PS_PARTKEY=P_PARTKEYand PS_SUPPKEY =S_SUPPKEYand PS_AVAILQTY > 2000

and P_BRAND = 'Brand#12'

and S_NATIONKEY = 1;

group by PS_AVAILQTY,PS_SUPPLYCOST,S_NAME;

按 PS_AVAILQTY,PS_SUPPLYCOST,S_NAME这三个属性分组,我们实现时使用了一个技巧,将每个候选记录的这三个字段按字符串格式拼接成一个新的属性,拼接的示例如下:

"4925" "854.19" "Supplier#000002515" -->> "4925+854.19+Supplier#000002515"

注意中间加了一个加号“+”,这个加号是必须的,如果没有加号,"105","201"与"10","5201"的拼接结果都是"105201",这样得到的group by结果将会出错,而添加一个加号它们两的拼接结果是不同的。

拼接后,我们只需要按新的属性进行分组,可以使用map来实现,map的key为新的属性值,value为新属性值key的后续记录。再在组上进行聚集函数的运算。

这个小项目就写到这里了,或许这压根只是一个数据处理,谈不上数据库实现,不过通过这个小项目我对数据库底层的实现还是了解了很多,以后做数据库优化理解起来也容易一些。

谢谢关注,欢迎评论。

两表联查分组_join多表连接和group by分组相关推荐

  1. mysql多表联查分页_sqlserver多表联合查询和多表分页查询的代码讲解

    sqlserver多表联合查询和多表分页查询的代码讲解 发布时间:2020-05-14 14:42:07 来源:亿速云 阅读:700 作者:Leah 这篇文章主要为大家详细介绍了sqlserver多表 ...

  2. mysql 多表联查_MySQL的多表联查

    今天是周二,我们一起来研究下MySQL的多表联查啊.或许你也知道,表之间的关系有:1对1.1对多.多对多.然后...... 1. 嵌套查询:一个查询的结果是另外sql查询的条件 如:查询stu表中年龄 ...

  3. java中mysql分组查询_ES对应mysql的group by分组查询javaApi,多对多关系的分组查询...

    ES对应mysql的group by分组查询javaApi,多对多关系的分组查询 比如我这边有个下列订单索引数据,现在的需求是按用户(fmerchantId)和支付方式(fchannelId)进行分组 ...

  4. mysql多表联查到新的表中_MySQL中的多表联查

    多表查询: 1. 笛卡尔积的形式; 2. 内连接的形式: 查询两个表中符合连接条件的记录; SELECT 字段名称,... FROM tbl_name1 INNER JOIN tbl_name2 ON ...

  5. jpa多表联查动态_jpa多表关联动态查询(自定义sql语句)

    项目中,jpa对于简单的数据库操作很方便,但如果多表关联动态查询时,需要自己去写SQL语句拼接查询条件,以下为本人学习的例子. 类似于这种多条件动态查询: 项目用的是springboot 2.1.0. ...

  6. jpa多表联查动态_jpa联表查询一对多

    数据库 表company id, name 表employee employee_id,employee_name,company_id,price,monthtime,amount 实体类 Comp ...

  7. mysql的四表联查_数据库四表联查

    21.查询不同老师所教不同课程平均分从高到低显示SELECT max(Z.T#) AS 教师ID,MAX(Z.Tname) AS 教师姓名,C.C# AS 课程ID,MAX(C.Cname) AS 课 ...

  8. 数据库(单表查询与多表联查)

    文章目录 一.单表查询 1.创建表 2.查询 二.多表联查 1.创建表 查询 一.单表查询 素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作 等 CREATE T ...

  9. oracle两表联查分组,oracle解决多表关联分组查询问题

    做了一个功能需要分组查询,同时查询A表分组查询的ID需要关联B表的数据,本来想两个表关联查询,但是报group by 语法不正确.所以做了以下修改. select count(*), cindexid ...

最新文章

  1. 模拟请求分页管理中地址转换和缺页中断处理_Linux内存管理:缺页异常(一)
  2. 【Verilog HDL 训练】第 02 天
  3. 腾讯云推出竞价实例 云服务器开销最高下降90%
  4. boost第 4 章 事件处理
  5. 网站SEO优化中内部链接的优化
  6. 深入 Linux PAM 体系结构
  7. 防抖、节流(细讲)【面试】
  8. 学习进度条__软件工程概论第一周学习计划
  9. 160906、Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
  10. unity双面显示shader
  11. 计算机WPS一级教材PDF,2017年计算机一级WPS辅导:金山词霸PDF文档取词攻略
  12. 如何使用IDEA进行协作编码,共享项目,并实时的处理
  13. 自定义Navigationbar,使用Catagory
  14. 3d渲染是显示计算机内存不足,win10系统使用3d渲染图片出现内存不足如何解决
  15. 基于matlab的mimo仿真,基于MATLAB的MIMO系统仿真与分析|Matlab代做
  16. 最新Quarters II 13.1 下载安装全教程 + ModelSim联调(2022/12/11 )
  17. WORD 分栏后 页码混乱
  18. win7 ftp安装搭建,并且上传图片到ftp文件夹下,使用nginx访问下载图片
  19. 如何学习自然语言处理:一本书和一门课
  20. CSAPP第五章家庭作业参考答案

热门文章

  1. SFP+万兆光纤收发器 万兆1光1电光纤收发器10G光纤交换机10G万兆光纤收发器单模万兆光纤收发器万兆网络收发器万兆网络光端机
  2. C++程序设计输入输出专题
  3. 突破重围,攻“新”为上!凯里亚德与郁锦香酒店以创新势能获投资者青睐
  4. 【LLM】Langchain使用[一](模型、提示和解析器、存储)
  5. 2023-07-013——或许大多数人都不是那么耀眼,但我认为当他或她能够独立又骄傲的撑起自己的天,也如明星般闪耀
  6. Android Studio未配置appkey或配置错误
  7. ffmpeg一键压特效+小丸工具箱压mod特效的方法
  8. 2021辽宁省赛——H制造游戏币
  9. 根据会员积分改变会员等级
  10. 技术泡妹子之SVG识别妹子身体各个部位