• 数据库SQL语句执行顺序


    一、前言

      在SQL语句中有很多的关键字,比如SELECT、FROM、JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN、ON、WHERE、GROUP、HAVING、ORDER BY、TOP、LIMIT、UNION、UNION ALL、MAX、MIN、COUNT。这些关键字有条件过滤、分组、排序、聚合函数、合并结果集,由这些关键字组成的SQL语句是按照什么样的顺序进行查询结果数据?

    二、SQL逻辑查询语句执行顺序

      如下定义一个查询的伪代码,每一个关键字注释一个序列号,其中1-6的序号表示执行先后顺序信息,相同序号内部的细分则是该序号内部的执行顺序,其他则是该序号并行次序执行。

    --查询组合字段
    (5)select (5-2) distinct(5-3) top(<top_specification>)(5-1)<select_list>
    --连表
    (1)from (1-J)<left_table><join_type> join <right_table> on <on_predicate>
    (1-A)<left_table><apply_type> apply <right_table_expression> as <alias>
    (1-P)<left_table> pivot (<pivot_specification>) as <alias>
    (1-U)<left_table> unpivot (<unpivot_specification>) as <alias>
    --查询条件
    (2)where <where_pridicate>
    --分组
    (3)group by <group_by_specification>
    --分组条件
    (4)having<having_predicate>
    --排序
    (6)order by<order_by_list>
    

    ps:join_type(表连接类型)、apply_type(表运算符类型)、pivot(行转列)、unpivot(列转行)。left join/right join/full join的空缺字段使用NULL值进行补齐。

    关键字执行描述
    <1>FROM:对FROM子句中的前两个表执行笛卡尔积(Cartesian product)(交叉联接),生成虚拟表VT1
    <2>ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2。
    <3>OUTER(JOIN):如果指定了OUTER JOIN(相对于CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部联接把左表标记为保留表,右外部联接把右表标记为保留表,完全外部联接把两个表都标记为保留表)中未找到匹配的行将作为外部行添加到 VT2,生成VT3.如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止。
    <4>WHERE:对VT3应用WHERE筛选器。只有使<where_condition>为true的行才被插入VT4.
    <5>GROUP BY:按GROUP BY子句中的列列表对VT4中的行分组,生成VT5.
    <6>CUBE|ROLLUP:把超组(Suppergroups)插入VT5,生成VT6.
    <7>HAVING:对VT6应用HAVING筛选器。只有使<having_condition>为true的组才会被插入VT7.
    <8>SELECT:处理SELECT列表,产生VT8.
    <9>DISTINCT:将重复的行从VT8中移除,产生VT9.
    <10>ORDER BY:将VT9中的行按ORDER BY 子句中的列列表排序,生成游标(VC10).
    <11>TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回调用者。

    ps:每一步执行都会产生相应的虚拟表,作为下一个步骤的结果集,直到完成所以SQL语句查询虚拟表为最终结果集。

    三、实例分析

    1、创建学生表和学生选课的成绩表

    DROP TABLE IF EXISTS `student`;
    CREATE TABLE `student` (
      `sno` int NOT NULL AUTO_INCREMENT COMMENT '学生号',
      `sname` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名',
      `ssex` int NOT NULL COMMENT '性别',
      `sage` int NOT NULL COMMENT '年龄',
      PRIMARY KEY (`sno`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3;
    
    INSERT INTO `student` VALUES ('1', '张三', '1', '18');
    INSERT INTO `student` VALUES ('2', '李四', '2', '19');
    INSERT INTO `student` VALUES ('3', '王五', '1', '20');
    INSERT INTO `student` VALUES ('4', '赵六', '1', '21');
    DROP TABLE IF EXISTS `score`;
    CREATE TABLE `score` (
      `sno` int NOT NULL COMMENT '学生号',
      `cno` int NOT NULL COMMENT '课程号',
      `mark` decimal(10,0) NOT NULL COMMENT '分数'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
    
    INSERT INTO `score` VALUES ('1', '1', '60');
    INSERT INTO `score` VALUES ('2', '1', '70');
    INSERT INTO `score` VALUES ('1', '2', '80');
    INSERT INTO `score` VALUES ('2', '2', '90');
    INSERT INTO `score` VALUES ('4', '1', '100');
    INSERT INTO `score` VALUES ('5', '1', '0');

     2、执行FROM语句生成笛卡尔积结果,会得到一个虚拟表(VT1)

    select * from  test.student join test.score

     ps:依据学生表的4条数据和课程成绩表的6条数据,产生笛卡尔积的4*6=24条数据,其结果作为下一关键字的筛选数据。

    3、执行ON关键字,ON关键字是过滤条件,过滤对应字段不符合ON条件的数据,比如a.sno=b.sno表示学生表学生号等于课程成绩表的学生号,不等于就过滤。

    select * from  test.student a join test.score b on a.sno=b.sno
    

    ps:通过ON关键字在笛卡尔积的产生VT1虚拟表的基础上过滤数据生成新的虚拟表VT2。 

    4、使用外部行(左连接、右连接、全连接)

    select * from  test.student a left join test.score b on a.sno=b.sno
    

     ps:使用外部行的工作就是在VT2表的基础上添加保留表中被过滤条件过滤掉的数据,非保留表中的数据被赋予NULL值,最后生成虚拟表VT3。

    5、执行where过滤条件

    select * from  test.student a left join test.score b on a.sno=b.sno where a.ssex=1
    

     ps:对虚拟表VT3的结果集中学生性别字段为不为1的数据过滤,最后生成虚拟表VT4。

    6、执行GROUP BY分组语句

    select a.sno,max(a.sname),count(a.sno),sum(b.mark) from  test.student a left join test.score b on a.sno=b.sno where a.ssex=1 group by a.sno
    

     ps:对虚拟表VT4的学生号进行分组,结果表示学生选课的数量count(a.sno),此时,我们就得到了一个VT5虚拟表。

    7、执行HAVING过滤条件

    select a.sno,max(a.sname),count(a.sno),sum(b.mark) from  test.student a left join test.score b on a.sno=b.sno where a.ssex=1 group by a.sno
    having count(a.sno)=1
    

     ps:配合分组VT5虚拟表的结果集,对count(a.sno)选课数量进行过滤,选择选择一门课的数据,生成虚拟表VT6。

    8、SELECT列表,对虚拟表VT6的数据选择指定字段的作为结果返回虚拟表VT7

    9、执行DISTINCT,对虚拟表VT6的结果集按照DISTINCT操作的列增加了一个唯一索引,以此来除重复数据,其一样是生成虚拟表

    10、执行ORDER BY子句

    select a.sno,max(a.sname),count(a.sno),sum(b.mark) from  test.student a left join test.score b on a.sno=b.sno where a.ssex=1 GROUP BY a.sno
    HAVING count(a.sno)=1 order by sum(b.mark) desc
    

     ps:对虚拟表VT7的结果集中字段sum(b.mark)进行排序,生成虚拟表VT8,直到所有关键字执行完成,返回查询的结果集。

    四、总结

    1、SQL语句查询顺序的过程是不断生成各种虚拟表,每一步的结果都是提供给下一步的虚拟表。

    2、SQL语句的执行过程,在分析器中会对语句的执行效率进行优化语句,所以其执行过程不一定是按照编写的SQL语句执行。

  • 相关阅读:
    ZeroMQ
    ps-lite源码解析
    RDMA
    MapReduce
    parameter server
    BytePS
    ELF程序头部及程序加载
    网络序与主机序
    CPU、CPU核与线程的关系
    chroot
  • 原文地址:https://www.cnblogs.com/tuqunfu/p/15567103.html
Copyright © 2020-2023  润新知