SQL查询优化分析

2020-11-28 07:39徐爱芸
西部论丛 2020年11期

徐爱芸

摘 要:SQL对数据执行最多的操作是对数据的查询,查询的方法不唯一,因此查询的速度和效率差别也很大。本文分析了几种查询的方法:相关子查询、无关子查询、集合的查询,分析查询执行的过程,并对每一种查询方法进行了定量的分析,从而得出了比较优化的查询方法。

关键词:SQL语句;相关子查询;无关子查询;集合查询;查询优化

1前言

子查询也称嵌套查询,它嵌套在 SELECT、INSERT、UPDATE、DELETE 语句或其它子查询中,任何允许使用表达式的地方都可以使用子查询。如果子查询的条件中使用了其外层的表的字段,这种子查询就叫作相关子查询。相关子查询可以用EXISTS、NOT EXISTS引入。子查询可以计算一个变化的聚合函数值,并返回到外围查询进行比较。子查询比较灵活、方便,常作为增删改查的筛选条件,适合于操纵一个表的数据。但是在性能上,往往一个不合适的子查询用法会形成一个性能瓶颈。

2子查询分类

一个查询语句块(select-from-where)可以嵌套在另外一个查询块的where子句中,从子查询的语法规则和执行过程不同,子查询分为无关子查询和相关子查询两种。

2.1无关子查询

无关子查询的结构如下:SELECT ——FROM——WHERE  (SELECT ——FROM——WHERE)

无关子查询的工作方式是由内向外处理:先执行内查询,外层查询利用内层查询的结果作条件,子查询返回结果的数据类型必须与外查询 WHERE 语句的数据类型相匹配。

2.2相关子查询

相关子查询的结构如下:SELECT ——FROM——WHERE  NOT  EXISTS (SELECT  *   FROM——  WHERE  NOT  EXISTS    (SELECT  * FROM —— WHERE   ));

带有EXISTS量词的子查询不返回任何数据,只产生逻辑值,子查询的查询条件依赖于外层父查询的某个属性值,这就是相关子查询的由来,内查询的执行次数与外查询执行次数相同。

3查询执行过程分析

以下面的三张表为例子,要求查询被所有学生都选修了的课程信息。

Student(Sno ,Sname,Sdep ) Course(Cno, Cname, Credit)  SC(Sno, Cno, Score)

3.1采用相关子查询

根据传统的方法,我们采用三层嵌套,对应的SQL语句如下:

SELECT Cno, Cname   FROM  Course   WHERE NOT EXISTS (SELECT  *  FROM Student WHERE NOT EXISTS     (SELECT * FROM SC WHERE Sno=Student.Sno AND Cno=Course.Cno));

这个算是一个比较复杂的sql语句了,两个 NOT EXISTS和三个WHERE,这个sql语句可以分为3层:最外层语句,最内层语句,中间层语句。

其执行过程如下:①进入第一层:在外层Course中取出一个元组的Cno,如Cno值‘C1;②进入第二层:取Student表中的一个Sno值‘S1;③进入第三层:判断SC表中是否有 Cno=‘C1 并且Sno=‘S1这样的元组,经过NOT EXISTS的取反,存在F,不存在就是T—— 结果F④返回第二层:取第二个Sno值‘S2⑤进入第三层:判断是否存在 Cno=‘C1 并且Sno=‘S2这样的元组,结果取反 —— 结果F;⑥经过多次这样的迭代,取最后一个Sno值,结果取反 —— 结果F,返回第一层的结果:T(NOT EXISTS再次取反),所以将Cno='C1课程信息取出放入结果表中,即对应Course的'C1记录,Student表中的所有的记录对应的中间层的返回值为假,所以最外层的NOT EXISTS对应的值为真,最外层的WHERE的值也为真,则'C1对应的课程的记录符合查询条件,装入结果表中。

重复上述步骤,直到将Course中的元组取完。

这是一个带有全称量词的谓词查询(题中带有“全部”),将其转换为等价的存在量词的谓词查询,即:不存在一个学生没有选修这门课程的,即所有学生都选了这门课程。

可以看出:相关子查询从上往下顺序执行,主查询的每一行查询都执行一次完整的子查询。EXISTS 操作符检查在子查询中是否存在满足条件的行:如果在子查询中存在满足条件的行:不在子查询中继续查找,条件返回 TRUE;如果在子查询中不存在满足条件的行,条件返回 FALSE, 继续在子查询中查找。假设学生表有10条记录,课程表有8条记录,选课表按平均每人选6门课算,则整个查询要执行10×8×6=480次。

3.2 采用综合查询法

如果将外层设计为相关子查询,而在中间层为无关子查询,对应的SQL语句如下:SELECT Cno,Cname  FROM Course WHERE NOT EXISTS (SELECT * FROM Student WHERE Sno  NOT IN (SELECT Sno  FROM  SC  WHERE Cno=C.Cno)) ;

其执行过程如下:① 进入第一层:在外层Course中取出一个元组的Cno,如Cno值‘C1;② 进入第二层:执行无关子查询,按照无关子查询执行的顺序,先执行最内层的查询,在选课表中查询选修了C1课程的学生的学号,返回到中间层;③ 经过NOT  IN 的取反,得到的是没有选修‘C1课程的学生的学号;④ 根据相关子查询的执行原理,将中间层的结果返回到第一层,再一次经过NOT EXISTS的取反,得到的是不存在没有选修‘C1课程的学生,说明所有学生都选修了‘C1课程。

可以看出:这里采用了相关子查询和无关子查询两种方法,同样按上面的例子,由于第二层和第三层之间采用的无关子查询,只需要在子查询中查找满足条件的元组,不需要外层查询中的字段值,整个查询要执行10×8=80次。显然,这种方法更好理解,且只有一层相关子查询,执行速度快。

3.3 采用集合的查詢

SQL的SELECT语句查询结果是元组的集合,如果将外层设计为相关子查询,而把内层设计为集合的查询,集合操作可以用差运算。这样三层嵌套查询可以用两层嵌套查询完成。对应的SQL语句如下:SELECT  Cno,Cname  FROM C  WHERE NOT EXISTS  (( SELECT  Sno   FROM S)    EXCEPT   (SELECT Sno  FROM  SC  )  WHERE Cno=C.Cno  ) ;

其执行过程如下:① 进入第一层:在外层Course中取出一个元组的Cno,如Cno值‘C1;② 进入第二层:执行集合的子查询,在学生表中投影全部学生学号Sno,同时在选课表中查选修了‘C1课程的学生学号,然后执行集合的差运算,得到的是没有选修‘C1课程的学生学号,返回到外层;③ 经过NOT  EXISTS的取反,得到的是不存在没有选修‘C1课程的学生,说明所有学生都选修了‘C1课程。

采用了相关子查询和集合查询,同样按上面的例子,只有两层查询,则整个查询要执行10次。显然,这种方法最简单,方法直接,易理解,执行速度最快,效率最高。

结束语

一个查询是可以用多种方法来实现的,一个复杂的查询要用到嵌套查询,相关子查询最不好理解,执行的过程长,效率低;如果采用无关子查询、集合查询等,可以加快查询的速度,提高查询效率。

参考文献

[1] 姜代红,蒋秀莲 .数据库原理及应用(第2版)清华大学出版社,2010.12

[2] 何玉洁 数据库系统教程(第2版)人民邮电出版社,2015.12