• mysql-4:SQL的解析顺序


    SQL语句的解析顺序

    编写顺序与解析顺序

    • 编写顺序
    SELECT DISTINCT
    < select_list >
    FROM
    < left_table > < join_type >
    JOIN < right_table > ON < join_condition >
    WHERE
    < where_condition >
    GROUP BY
    < group_by_list >
    HAVING
    < having_condition >
    ORDER BY
    < order_by_condition >
    LIMIT < limit_number >
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 解析顺序
    -- 行过滤
    1 FROM <left_table>
    2 ON <join_condition>
    3 <join_type> JOIN <right_table> 第二步和第三步会循环执行
    4 WHERE <where_condition> 第四步会循环执行,多个条件的执行顺序是从左往右的。
    5 GROUP BY <group_by_list>
    6 HAVING <having_condition>
    --列过滤
    7 SELECT 分组之后才会执行SELECT
    8 DISTINCT <select_list>
    --排序
    9 ORDER BY <order_by_condition>
    -- MySQL附加
    10 LIMIT <limit_number>9步都是SQL92标准语法。limit是MySQL的独有语法。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解析顺序关键字

    FROM

    • 对FROM的左边的表和右边的表计算 笛卡尔积(CROSS JOIN) 。产生虚表VT1:
    mysql> select * from product,category;
    +-----+-----------------+-------+-------+------+----+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+-----------------+-------+-------+------+----+--------------+
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 |
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 2 | 国内食品 |
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 3 | 国内服装 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 2 | 国内食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 3 | 国内服装 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 1 | 国外食品 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 3 | 国内服装 |
    | 4 | 十三香 | 10 | NULL | 2 | 1 | 国外食品 |
    | 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 |
    | 4 | 十三香 | 10 | NULL | 2 | 3 | 国内服装 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 1 | 国外食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 3 | 国内服装 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | 1 | 国外食品 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | 2 | 国内食品 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | 3 | 国内服装 |
    | 7 | iPhone手机 | 800 | NULL | 30 | 1 | 国外食品 |
    | 7 | iPhone手机 | 800 | NULL | 30 | 2 | 国内食品 |
    | 7 | iPhone手机 | 800 | NULL | 30 | 3 | 国内服装 |
    +-----+-----------------+-------+-------+------+----+--------------+
    21 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    通过在 FROM 子句中列出多个表格,你可以执行一个叫做交叉连接(Cross Join)的操作,它会将product,category两个表的每一行进行组合,生成一个包含所有可能组合的结果集。

    ON

    • 对虚表VT1进行ON筛选,只有那些符合的行才会被记录在虚表VT2中:
    mysql> select * from product a, category b where a.cid=b.id;
    +-----+-----------------+-------+-------+------+----+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+-----------------+-------+-------+------+----+--------------+
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 |
    | 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    +-----+-----------------+-------+-------+------+----+--------------+
    5 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    OUTER JOIN

    • 如果指定了 OUTER JOIN(比如left join、 right join) ,那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2 中,产生虚拟表VT3
    • 如果FROM子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤,一直到处理完所有的表为止
    • 以左表数据为准
    mysql> select * from product a left outer join category b on a.cid=b.id; 
    +-----+-----------------+-------+-------+------+------+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+-----------------+-------+-------+------+------+--------------+
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 |
    | 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL |
    | 7 | iPhone手机 | 800 | NULL | 30 | NULL | NULL |
    +-----+-----------------+-------+-------+------+------+--------------+
    7 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 以右表数据为准
    mysql> select * from product a right outer join category b on a.cid=b.id; 
    +------+-----------------+-------+-------+------+----+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +------+-----------------+-------+-------+------+----+--------------+
    | 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 |
    | 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | NULL | NULL | NULL | NULL | NULL | 3 | 国内服装 |
    +------+-----------------+-------+-------+------+----+--------------+
    6 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    WHERE

    • 对虚拟表VT3 进行WHERE条件过滤。只有符合的记录才会被插入到虚拟表VT4 中

    此时因为分组,不能使用聚合运算;也不能使用SELECT中创建的别名;

    • 与ON的区别
      • 如果有外部列,ON针对过滤的是关联表,主表(保留表)会返回所有的列;
      • 如果没有添加外部列,两者的效果是一样的;
    • 应用
      • 对主表的过滤应该放在WHERE;
      • 对于关联表,先条件查询后连接则用ON,先连接后条件查询则用WHERE;
    mysql> select * from product a left outer join category b on a.cid=b.id where
    a.pname='泰国大枣';
    +-----+--------------+-------+-------+------+------+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+--------------+-------+-------+------+------+--------------+
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL |
    +-----+--------------+-------+-------+------+------+--------------+
    3 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    GROUP BY

    • 根据group by子句中的列,对VT4中的记录进行分组操作,产生虚拟表VT5。

    注意:其后处理过程的语句,如SELECT,HAVING,所用到的列必须包含在GROUP BY中。对于没有出现的,得用聚合函数;

    • 原因:
      • GROUP BY改变了对表的引用,将其转换为新的引用方式,能够对其进行下一级逻辑操作的列会减少;
    • 理解
      • 根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换成单值;
    mysql> select * from product a left outer join category b on a.cid=b.id where
    a.pname='泰国大枣' group by a.price;
    +-----+--------------+-------+-------+------+------+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+--------------+-------+-------+------+------+--------------+
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL |
    +-----+--------------+-------+-------+------+------+--------------+
    3 rows in set (0.01 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    HAVING

    • 对虚拟表VT5 应用having过滤,只有符合的记录才会被插入到虚拟表VT6 中。
    mysql> select * from product a left outer join category b on a.cid=b.id where
    a.pname='泰国大枣' group by a.price having b.id <=2;
    +-----+--------------+-------+-------+------+------+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+--------------+-------+-------+------+------+--------------+
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    +-----+--------------+-------+-------+------+------+--------------+
    2 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    SELECT

    • 这个子句对SELECT子句中的元素进行处理,生成VT5表。
    • (5-J1)计算表达式计算SELECT 子句中的表达式,生成VT5-J1

    DISTINCT

    • 寻找VT5-1中的重复列,并删掉,生成VT5-J2
    • 如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘了)。这张临时表的表结构和上一步产生的虚拟表VT5是一样的,不同的是对进行DISTINCT操作的列增
      加了一个唯一索引,以此来除重复数据。
    mysql> select distinct a.pname from product a left outer join category b on
    a.cid=b.id where a.pname='泰国大枣' group by a.price ;
    +--------------+
    | pname |
    +--------------+
    | 泰国大枣 |
    +--------------+
    1 row in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ORDER BY

    • 从 VT5-J2 中的表中,根据ORDER BY 子句的条件对结果进行排序,生成VT6表

    唯一可使用SELECT中别名的地方

    mysql> select * from product a left outer join category b on a.cid=b.id where
    a.pname='泰国大枣' group by a.price having b.id <=2 order by b.id;
    +-----+--------------+-------+-------+------+------+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+--------------+-------+-------+------+------+--------------+
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    | 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |
    +-----+--------------+-------+-------+------+------+--------------+
    2 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    LIMIT

    • LIMIT子句从上一步得到的 VT6虚拟表 中选出从指定位置开始的指定行数据

    MySQL特有
    offset 和 rows 的正负带来的影响;

    • 当偏移量很大时效率是很低的,可以这么做:
      • 采用子查询的方式优化 ,在子查询里先从索引获取到最大id,然后倒序排,再取N行结果集
      • 采用INNER JOIN优化 ,JOIN子句里也优先从索引获取ID列表,然后直接关联查询获得最终结果
    mysql> select * from product a left outer join category b on a.cid=b.id where
    a.pname='泰国大枣' group by a.price having b.id <=2 order by b.id limit 1;
    +-----+--------------+-------+-------+------+-
    -----+--------------+
    | pid | pname | price | pdate | cid | id | cname |
    +-----+--------------+-------+-------+------+------+--------------+
    | 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |
    +-----+--------------+-------+-------+------+------+--------------+
    1 row in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析流程

    在这里插入图片描述

    流程分析

      1. FROM(将最近的两张表,进行笛卡尔积)—VT1
      1. ON(将VT1按照它的条件进行过滤)—VT2
      1. LEFT JOIN(保留左表的记录)—VT3
      1. WHERE(过滤VT3中的记录)–VT4…VTn
      1. GROUP BY(对VT4的记录进行分组)—VT5
      1. HAVING(对VT5中的记录进行过滤)—VT6
      1. SELECT(对VT6中的记录,选取指定的列)–VT7
      1. ORDER BY(对VT7的记录进行排序)–VT8
      1. LIMIT(对排序之后的值进行分页)–MySQL特有的语法
    流程说明
    • 单表查询:根据 WHERE 条件过滤表中的记录,形成中间表(这个中间表对用户是不可见的);然后根据 SELECT 的选择列选择相应的列进行返回最终结果。
    • 两表连接查询:对两表求积(笛卡尔积)并用 ON 条件和连接类型进行过滤形成中间表;然后根据WHERE条件过滤中间表的记录,并根据 SELECT 指定的列返回查询结果。

    笛卡尔积:行相乘、列相加。

    • 多表连接查询:先对第一个和第二个表按照两表连接做查询,然后用查询结果和第三个表做连接查询,以此类推,直到所有的表都连接上为止,最终形成一个中间的结果表,然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。
    WHERE条件解析顺序
    • MySQL :从左往右去执行 WHERE 条件的。
    • Oracle :从右往左去执行 WHERE 条件的。

    写WHERE条件的时候,优先级高的部分要去编写过滤力度最大的条件语句

  • 相关阅读:
    G120变频器输入输出端子功能定义配置方法及示例
    鉴源论坛 · 观擎丨基于模型的方法在民机机载软件中的应用
    与京东物流合作,能不能补全东方甄选的最后一块拼图?
    Lumerical官方案例、FDTD时域有限差分法仿真学习(十七)——Y分支功分器
    TVS专业术语解读
    数据结构-ArrayList解析和实现代码
    HANA Calculation View中的cross client
    Java 面试真题
    图扑 HT for Web 风格属性手册教程
    【JavaScript 逆向】猿人学 web 第五题:乱码增强
  • 原文地址:https://blog.csdn.net/weixin_44178305/article/details/133267701