乐趣区

关于后端:SQL-查询的执行顺序

by emanjusaka from https://www.emanjusaka.top/archives/6 彼岸花开可奈何
本文欢送分享与聚合,全文转载请留下原文地址。

前言

理解 SQL 查问的执行程序对咱们解决一些问题很有帮忙,有时咱们可能会纳闷为什么不能对分组的后果进行筛选这样相似的问题?之前始终不是了解这个问题,在理解了 SQL 查问的执行程序之后这个问题也就迎刃而解。在咱们对 SQL 查问语句进行剖析优化时,把握执行程序也是有肯定帮忙的。

一、实践程序

​​

下面是图示 SQL 的执行程序,上面用列表列出:

  1. FROM
  2. ON
  3. JOIN
  4. WHERE
  5. GROUP BY
  6. CUBE | ROLLUP
  7. HAVING
  8. SELECT
  9. DISTINCT
  10. ORDER BY
  11. LIMIT

下面所列出的执行程序能帮忙咱们解答一些问题:

  • 为啥不能对窗口函数的执行后果进行过滤?

    因为窗口函数在 SELECT 步骤执行,而这步是在 WHERE 和 GROUP BY 之后

  • 能够对分组的后果进行筛选吗?

    不能够,因为 GROUP BY 在 WHERE 之后执行

  • 能够对分组后的后果进行排序吗?

    能够,因为 ORDER BY 在 GROUP BY 之后。

二、代码示例

  • 学生表

    ​​

<!—->

  • 成绩表

    ​​

<!—->

  • 查问语句

    查问来自天津且总成绩高于 70 分,并且查问他们的总成绩,查问后果按问题降序排列

    SELECT
        ss.student_id,sum(se.grade) as total,ss.city
    FROM
        students ss
        LEFT JOIN score se ON ss.student_id = se.student_id 
    WHERE
        ss.city = "天津"
    GROUP BY ss.student_id
    HAVING sum(se.grade) > 70
    ORDER BY total DESC
    LIMIT 10

<!—->

  • 查问后果

    ​​

三、剖析 SQL 执行过程

SQL 运行的每个操作都会产生一张虚构表,只不过这些虚构表对用户是通明的,只有最初一步生成的虚构表才会返回给用户。

  1. 第一步执行的是对 FROM 字句前后的两张表 students 和 score 进行笛卡尔积操作,生成虚构表 VT1。
  2. 利用 ON 过滤器

    在虚构表 VT1 中执行过滤操作,过滤条件为:ss.student_id = se.student_id ​

    对于在 ON 过滤条件下的 NULL 值比拟,此时的比拟后果为 UNKNOWN,却被视为 FALSE 来进行解决,即两个 NULL 并不相同。然而在上面两种状况下认为两个 NULL 值的比拟是相等的:

    • GROUP BY 子句把所有 NULL 值分到同一组
    • ORDER BY 子句中把所有 NULL 值排列在一起

    在产生虚构表 VT2 时,会减少一个额定的列来示意 ON 过滤条件的返回值,返回值有 TRUE、FALSE、UNKNOWN。取出比拟值为 TRUE 的记录,产生虚构表 VT2。

  3. 增加内部行

    这一步只有在连贯类型为 OUTER JOIN 时才产生,如 LEFT OUTER JOIN、RIGHT OUTERJOIN、FULL OUTER JOIN。尽管在大多数时候咱们能够省略 OUTER 关键字,但 OUTER 代表的就是内部行。LEFT OUTER JOIN 把左表记为保留表,RIGHT OUTER JOIN 把右表记为保留表,FULL OUTER JOIN 把左右表都记为保留表。增加内部行的工作就是在 VT2 表的根底上增加保留表中被过滤条件过滤掉的数据,非保留表中的数据被赋予 NULL 值,最初生成虚构表 VT3

  4. 利用 WHERE 过滤器

    对上一步骤产生的虚构表 VT3 进行 WHERE 条件过滤,只有合乎 <where_condition> 的记录才会输入到虚构表 VT4 中

    在以后利用 WHERE 过滤器时,有两种过滤是不被容许的:

    • 因为数据还没有分组,因而当初还不能在 WHERE 过滤器中应用 where_condition=MIN(col) 这类对统计的过滤
    • 因为没有进行列的选取操作,因而在 SELECT 中应用列的别名也是不被容许的,如 SELECT city as c from students WHERE c = ‘ 天津 ’ 是不容许呈现的
  5. 分组

    在本步骤中依据指定的列对上个步骤中产生的虚构表进行分组,最初失去虚构表 VT5

  6. 利用 HAVING 过滤器

    在该步骤中对于上一步产生的虚构表利用 HAVING 过滤器,HAVING 是对分组条件进行过滤的筛选器。生成虚构表 VT6。

  7. 解决 SELECT 列表

    在这一步中,将 SELECT 中指定的列从上一步产生的虚构表中选出生成虚构表 VT7。

  8. 利用 ORDER BY 字句

    依据 ORDER BY 子句中指定的列对上一步输入的虚构表进行排列,返回新的虚构表 VT8。

    留神:在 MySQL 数据库中,NULL 值在升序过程中总是首先被选出,即 NULL 值在 ORDER BY 子句中被视为最小值

  9. LIMIT 子句

    在该步骤中利用 LIMIT 子句,从上一步骤的虚构表中选出从指定地位开始的指定行数据。对于没有利用 ORDER BY 的 LIMIT 子句,后果同样可能是无序的,因而 LIMIT 子句通常和 ORDER BY 子句一起应用。

四、留神

下面所探讨的程序皆为实践上的执行程序,实际上数据库引擎并不是通过连贯、过滤和分组来运行查问,因为它实现了一系列优化来晋升查问速度(不影响最终的返回后果)。数据库引擎可能会为了进步查问的速度把一些过滤条件进行提前,当然前提是不会对返回的后果造成影响。

SELECT
    ss.student_id,sum(se.grade) as total,ss.city
FROM
    students ss
    LEFT JOIN score se ON ss.student_id = se.student_id 
WHERE
    ss.city = "天津"

这个 sql 学生城市是天津的只有三个,如果在学生很多的状况下如果先做城市的筛选后再对两张表做笛卡尔积能够很大水平的晋升性能,并且也不会对返回的后果造成影响。这时实际上 SQL 的执行程序可能就与实践上的执行程序不一样了。

参考资料

  1. SQL queries don’t start with SELECT

本文原创,满腹经纶,如有纰漏,欢送斧正。尊贵的敌人,如果本文对您有所帮忙,欢送点赞,并期待您的反馈,以便于一直优化。

原文地址:https://www.emanjusaka.top/archives/6

微信公众号:emanjusaka 的编程栈

退出移动版