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

一、多表连接

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

  1、双表等值join

  我们看这样一个连接sql:

select PS_AVAILQTY,PS_SUPPLYCOST,S_NAME
from SUPPLIER,PARTSUPP
where PS_SUPPKEY = S_SUPPKEY and 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:
select PS_AVAILQTY,PS_SUPPLYCOST,S_NAME
from SUPPLIER,PARTSUPP
where PS_SUPPKEY = S_SUPPKEY
and 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 > 2000
Quering: SUPPLIER.S_NATIONKEY = 1
Quering: PARTSUPP.PS_SUPPKEY = SUPPLIER.S_SUPPKEY Output:
The result hava 26322 rows, here is the fisrt 10 rows:
-------------------------------------------------
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:

select PS_AVAILQTY,PS_SUPPLYCOST,S_NAME
from SUPPLIER,PART,PARTSUPP
where PS_PARTKEY = P_PARTKEY
and PS_SUPPKEY = S_SUPPKEY
and PS_AVAILQTY > 2000
and P_BRAND = 'Brand#12'
and S_NATIONKEY = 1;

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

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

  

  主要代码如下:

 sortJoin(joina,cloumi)#cloumi表示公共表在joina的列号
sortJoin(joinb,cloumj)#cloumj表示公共表在joinb的列号
i = j = 0#左右指针初试为0
while i < len(joina) and j < len(joinb):
if joina[i][cloumi] < joinb[j][cloumj]:
i += 1
elif joina[i][cloumi] > joinb[j][cloumj]:
j += 1
else:#相等,进行连接
lastj = j
while j < len(joinb) and joina[i][cloumi] == joinb[j][cloumj]:
temp = joina[i] + joinb[j]
temp.remove(joina[i][cloumi])#删掉重复的元素
mergeResult.append(temp)
j += 1
j = lastj#右指针回滚
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_NAME
from SUPPLIER,PART,PARTSUPP
where PS_PARTKEY = P_PARTKEY
and PS_SUPPKEY = S_SUPPKEY
and 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', '>', ''],
['PART.P_BRAND', '=', 'Brand#12'],
['SUPPLIER.S_NATIONKEY', '=', ''],
['PARTSUPP.PS_PARTKEY', '=', 'PART.P_PARTKEY'],
['PARTSUPP.PS_SUPPKEY', '=', 'SUPPLIER.S_SUPPKEY']]}
Quering: PARTSUPP.PS_AVAILQTY > 2000
Quering: PART.P_BRAND = Brand#
Quering: SUPPLIER.S_NATIONKEY = 1
Quering: PARTSUPP.PS_PARTKEY = PART.P_PARTKEY
Quering: PARTSUPP.PS_SUPPKEY = SUPPLIER.S_SUPPKEY Output:
The result hava 1022 rows, here is the fisrt 10 rows:
-------------------------------------------------
rows PARTSUPP.PS_AVAILQTY PARTSUPP.PS_SUPPLYCOST SUPPLIER.S_NAME
-------------------------------------------------
1 4925 854.19 Supplier#
2 4588 455.04 Supplier#
3 8830 852.13 Supplier#
4 8948 689.89 Supplier#
5 3870 488.38 Supplier#
6 6968 579.03 Supplier#
7 9269 228.31 Supplier#
8 8818 180.32 Supplier#
9 9343 785.01 Supplier#
10 3364 545.25 Supplier#
-------------------------------------------------
Take 50.42 seconds.

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

二、group by分组

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

select PS_AVAILQTY,PS_SUPPLYCOST,S_NAME,COUNT(*)
from SUPPLIER,PART,PARTSUPP
where PS_PARTKEY = P_PARTKEY
and PS_SUPPKEY = S_SUPPKEY
and 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的后续记录。再在组上进行聚集函数的运算。

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

  谢谢关注,欢迎评论。   

作者:MyDetail

出处:http://www.cnblogs.com/fengfenggirl/

本文版权归作者MyDetail和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

python实现简易数据库之三——join多表连接和group by分组的更多相关文章

  1. join多表连接和group by分组

    join多表连接和group by分组 上一篇里面我们实现了单表查询和top N查询,这一篇我们来讲述如何实现多表连接和group by分组. 一.多表连接 多表连接的时间是数据库一个非常耗时的操作, ...

  2. python实现简易数据库之二——单表查询和top N实现

    上一篇中,介绍了我们的存储和索引建立过程,这篇将介绍SQL查询.单表查询和TOPN实现. 一.SQL解析 正规的sql解析是用语法分析器,但是我找了好久,只知道可以用YACC.BISON等,sqlit ...

  3. MySQL JOIN 多表连接

    除了常用的两个表连接之外,SQL(MySQL) JOIN 语法还支持多表连接.多表连接基本语法如下: 1 ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON ...

  4. Oracle数据库,join多表关联方式、union结果集合并

    join on :   多表关联 内连接 :与其他表连接 from 表1 t join 表2 s on t.字段1 =s.字段2 join 表3 n on n.字段3=t.字段1 或 from 表1 ...

  5. MySql left join 多表连接查询优化语句

    先过滤条件然后再根据表连接 同时在表中建立相关查询字段的索引这样在大数据多表联合查询的情况下速度相当快 创建索引: create index ix_register_year ON dbo.selec ...

  6. sql之表连接和group by +组函数的分析

    1.首先我们来先看一个简单的例子: 有[Sales.Orders]订单表和[Sales.Customers]顾客表,表的机构如下 业务要求:筛选  来自“按时打算”国家的用户以及所下的订单数 sele ...

  7. python实现简易数据库之一——存储和索引建立

    最近没事做了一个数据库project,要求实现一个简单的数据库,能满足几个特定的查询,这里主要介绍一下我们的实现过程,代码放在过ithub,可参看这里.都说python的运行速度很慢,但因为时间比较急 ...

  8. c# DataTable join 两表连接

    转:https://www.cnblogs.com/xuxiaona/p/4000344.html JlrInfodt和dtsource是两个datatable,通过[姓名]和[lqry]进行关联 v ...

  9. 数据库多表连接方式介绍-HASH-JOIN

    1.概述 hash join是一种数据库在进行多表连接时的处理算法,对于多表连接还有两种比较常用的方式:sort merge-join 和 nested loop. 为了比较清楚的介绍hash joi ...

随机推荐

  1. Spring-2-B Save the Students(SPOJ AMR11B)解题报告及测试数据

    Save the Students Time Limit:134MS     Memory Limit:0KB     64bit IO Format:%lld & %llu   Descri ...

  2. Force.com平台基础

    当我开始写这篇博客的时候,<Force.com Platform Fundamentals>的PDF版本我还没看到十分之一.由于PDF全部是英文的,所以我看起来比较吃力.我尝试过边看边做笔 ...

  3. strcat()函数常见问题

    strcat(char *_Destination,const char *_Source)函数的功能是将后一个字符串粘贴到前一个字符串的末尾 原型 char *strcat(char *_Desti ...

  4. Aptana studio 3前端开发编辑器推荐

    直接进入主题,先上图 这就是我Apatana studio 3的默认界面,推荐此工具的原因主要有以下几点: 1.可以集成Emmet,快速编写HTML+CSS,做到效率倍增. 2.Jquery 自动完成 ...

  5. hdu 2196 Computer(树形DP)

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  6. UEdit初始化加载内容偶尔失败,解决

    var ue1 = UE.getEditor('aaa',{toolbars: [],autoHeightEnabled: false}); $(function(){ var content =$( ...

  7. Centos7 配置网络步奏详解

    Centos7 配置网络步奏详解 编辑网卡配置文件 vi /etc/sysconfig/network-script/ifcfg-ens01 备注:这里的ens01不是所有系统都叫这个,有的可能叫其他 ...

  8. 在AngularJS中实现自定义表单验证

    除了一些已经定义好了的验证(例如 必填项.最小长度.最大长度)之外,更常用的,还是需要我们自己定义表单验证,这样才能对于项目中遇到的很多非常规问题给出自己的合适的解决方案. 在表单中控制变量 表单的属 ...

  9. FileInputFormat

    MapReduce框架要处理数据的文件类型 FileInputFormat这个类决定. TextInputFormat是框架默认的文件类型,可以处理Text文件类型,如果你要处理的文件类型不是Text ...

  10. 再学TSQL基础--单表查询

    本内容是我学习tsql2008的阅读笔记 什么是关系模型? 若对列创建唯一约束,背后中的物理机制也是创建了一个唯一索引. SQL语句的逻辑解析顺序是FROM WHERE GROUP BY HAVING ...