很多 SQL 查询确实以 SELECT 开始(本文仅涉及 SELECT 查询,而不涉及 INSERT 或其他内容)。不过,我在网上搜索 ‘是否可以对窗口函数返回的结果进行过滤’ 这个问题,或者说可以在 WHERE、HAVING 或其他中过滤窗口函数的结果吗?最终我得出的结论是:窗口函数必须在 WHERE 和 GROUP BY 发生之后才能运行,所以答案是我们这样做。于是又引出了另一个问题:SQL 查询的执行顺序是什么样的?
直觉上这个问题应该很好回答,毕竟我自己已经至少写了 10000 个 SQL 查询了,其中还有一些是很复杂。但事实是,我仍然很难准确地说出它的执行顺序是什么样的。
我研究了一下,执行顺序如下所示。SELECT 并不是第一个执行的,而是第五个。
执行顺序如下:
这张图与 SQL 查询语义相关,让我们可以推理出给定查询返回的内容,并回答如下问题:
实际上,数据库引擎并不一定按照这个顺序执行查询,因为为了使查询运行更快,实现了一系列优化。所以:
Twitter上的有人指出,许多 SQL 可以使用如下语法实现:
SELECT CONCAT(first_name, ' ', last_name) AS full_name, count(*)
FROM table
GROUP BY full_name
上面的查询看起来 GROUP BY 发生在 SELECT 之后,因为 GROUP BY 引用了 SELECT 中的一个别名。实际上并不需要让 GROUP BY 发生在 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'
按照上图执行顺序我们知道:FROM / LEFT JOIN / ON 会先执行,然后是 WHERE, 最后是 SELECT。
如果只需要查找名为’mr darcy’的猫,那就没必要对两张表的所有行进行左连接,先对猫名为 ‘mr darcy’ 执行过滤会更快。在这种情况下,先执行过滤不会改变查询结果!
在实践中,数据库引擎还会有很多其他优化措施,这些优化措施可能会使它们以不同的顺序执行查询,因为我不是这方面的专家,所以在这不展开介绍。