SQL查询总是先执行SELECT语句么?

InfoQ上看到了Julia Evans这篇《SQL queries don't start with SELECT》文章的译文,有点意思。

译文链接:

https://www.infoq.cn/article/Oke8hgilga3PTZ3gWvbg

原文链接:

https://jvns.ca/blog/2019/10/03/sql-queries-don-t-start-with-select/

很多SQL查询都是以SELECT开始的。不过,最近我跟别人解释什么是窗口函数,我在网上搜索”是否可以对窗口函数返回的结果进行过滤“这个问题,得出的结论是”窗口函数必须在WHERE和GROUP BY之后,所以不能”。

于是我又想到了另一个问题:SQL查询的执行顺序是怎样的?

好像这个问题应该很好回答,毕竟自己已经写了上万个SQL查询了,有一些还很复杂。但事实是,我仍然很难确切地说出它的顺序是怎样的。

SQL查询的执行顺序

于是我研究了一下,发现顺序大概是这样的。SELECT并不是最先执行的,而是在第五个。

SQL查询总是先执行SELECT语句么?_第1张图片

这张图回答了以下这些问题

这张图与SQL查询的语义有关,让你知道一个查询会返回什么,并回答了以下这些问题:

  • 可以在GROUP BY之后使用WHERE吗?(不行,WHERE是在GROUP BY之后!)

  • 可以对窗口函数返回的结果进行过滤吗?(不行,窗口函数是SELECT语句里,而SELECT是在WHERE和GROUP BY之后)

  • 可以基于GROUP BY里的东西进行ORDER BY吗?(可以,ORDER BY基本上是在最后执行的,所以可以基于任何东西进行ORDER BY)

  • LIMIT是在什么时候执行?(在最后!)

但数据库引擎并不一定严格按照这个顺序执行SQL查询,因为为了更快地执行查询,它们会做出一些优化,这些问题会在以后的文章中解释。

所以:

  • 如果你想要知道一个查询语句是否合法,或者想要知道一个查询语句会返回什么,可以参考这张图;

  • 在涉及查询性能或者与索引有关的东西时,这张图就不适用了。

混合因素:列别名

有很多SQL实现允许你使用这样的语法:

SELECT CONCAT(first_name, ' ', last_name) AS full_name, count(*)
FROM table
GROUP BY full_name

从这个语句来看,好像GROUP BY是在SELECT之后执行的,因为他引用了SELECT中的一个别名。但实际上不一定要这样,数据库引擎可以把查询重写成这样:

SELECT CONCAT(first_name, ' ', last_name) AS full_name, count(*)
FROM table
GROUP BY CONCAT(first_name, ' ', last_name)

这样GROUP BY仍然先执行。

数据库引擎还会做一系列检查,确保SELECT和GROUP BY中的东西是有效的,所以会在生成执行计划之前对查询做一次整体检查。

数据库可能不按照这个顺序执行查询(优化)

在实际当中,数据库不一定会按照JOIN、WHERE、GROUP BY的顺序来执行查询,因为他们会进行一系列优化,把执行顺序打乱,从而让查询执行得更快,只要不改变查询结果。

这个查询说明了为什么需要以不同的顺序执行查询:

SELECT * FROM
owners LEFT JOIN cats ON owners.id = cats.owner
WHERE cats.name = 'mr darcy'

如果只需要找出名字叫“mr darcy”的猫,那就没必要对两张表的所有数据执行左连接,在连接之前先进行过滤,这样查询会快得多,而且对于这个查询来说,先执行过滤并不会改变查询结果。

数据库引擎还会做出其他很多优化,按照不同的顺序执行查询,不过我并不是这方面的专家,所以这里就不多说了。

LINQ的查询以FROM开头

LINQ(C#和VB.NET中的查询语法)是按照FROM…WHERE…SELECT的顺序来的。这里有一个LINQ查询例子:

var teenAgerStudent = from s in studentList
                      where s.Age > 12 && s.Age < 20
                      select s;

pandas中的查询也基本上是这样的,不过你不一定要按照这个顺序。我通常会像下面这样写pandas代码:

df = thing1.join(thing2)      # JOIN
df = df[df.created_at > 1000] # WHERE
df = df.groupby('something', num_yes = ('yes', 'sum')) # GROUP BY
df = df[df.num_yes > 2]       # HAVING, 对 GROUP BY 结果进行过滤
df = df[['num_yes', 'something1', 'something']] # SELECT, 选择要显示的列
df.sort_values('sometthing', ascending=True)[:30] # ORDER BY 和 LIMIT
df[:30]

这样写并不是因为pandas规定了这些规则,而是按照JOIN/WHERE/GROUP BY/HAVING这样的顺序来写代码会更有意义些。不过我经常会先写WHERE来改进性能,而且我想大多数数据库引擎也会这么做。


近期的热文:

《Oracle删除字段的方式和风险,你都了解么?》

《了解阿克曼转向原理的作用》

《登录缓慢的诡异问题》

《不可不知的7个JDK命令》

《一个Full GC次数过多导致系统CPU 100%的案例排查》

《Oracle相关提问的智慧技巧》

《很久以前的一篇对初学Oracle建议的文章》

《PLSQL Developer几个可能的隐患》

《从70万字SRE神作提炼出的7千字精华文章》

《从数据误删到全量恢复的惊险记录》

《decode函数再挖掘》

《《decode函数的妙用》网友的两个问题解答》

《decode函数的妙用》
《公众号600篇文章分类和索引》

你可能感兴趣的:(SQL查询总是先执行SELECT语句么?)