SQL 执行顺序:它是否存在?

问题描述

我真的对 sql 中的执行顺序感到困惑。基本上,给定任何查询(假设它是一个具有多个 JOINSWHERE 子句等的复杂查询),该查询是否按顺序执行?

Order Of Execution of the SQL query 的最佳答案来看,似乎“sql 没有执行顺序。...优化器可以自由选择任何合适的顺序来产生最佳执行时间。”

相比之下,从 What's the execute order of the different parts of a SQL select statement? 的顶部答案中,我们在表单中看到了清晰的执行顺序 "

  1. 来自
  2. 开启
  3. 外部
  4. 哪里 ... "

我觉得我遗漏了一些东西,但这两个帖子似乎相互矛盾,而且网上不同的文章似乎支持其中一个

但更根本的是,我最初想知道的是:假设我们有一个复杂的 sql 查询,其中包含多个连接,INNER JOINsLEFT JOINS,按特定顺序排列。查询是否会有顺序,以便稍后的 JOIN 将应用于较早联接的结果,而不是应用于顶部 {{1} 中指定的初始表} 条款?

解决方法

这很棘手。简短的回答是:DBMS 将决定哪种顺序是最好的,以便它产生您声明的结果(请记住,SQL 是声明性的,它没有规定如何计算查询)。

但我们可以想到 DBMS 将用来创建结果的“概念性”执行顺序。这个概念顺序可能会被 DBMS 完全忽略,但如果我们(人类)遵循它,我们将得到与 DBMS 相同的结果。我认为这是 DBMS 的好处之一。即使我们写了一个低效的查询,DBMS 可能会说,“不,不,你给我的这个查询在性能方面很糟糕,我知道如何做得更好”,而且大多数时候,DBMS 是正确的。有时并非如此,重写查询有助于 DBMS 找到“最佳”方法。这当然非常依赖于 DBMS...

这个概念顺序帮助我们(人类)理解 DBMS 如何执行查询。下面列出了这些。

首先是非聚合的顺序:

  1. 执行 FROM 部分。包括任何联接、交叉产品、子查询。
  2. 执行 WHERE 子句(删除元组,这称为选择)
  3. 执行 SELECT 部分(报告结果,这称为投影)。

如果你使用聚合函数,那么没有分组:

  1. 执行 FROM 部分。包括任何连接、子查询。
  2. 执行 WHERE 子句(删除元组,这称为选择)
  3. 在SELECT部分​​做聚合函数(将结果的所有元组转化为一个元组)。此查询中有一个隐式 group by。

如果您使用组:

  1. 执行 FROM 部分。包括任何联接、交叉产品、子查询。
  2. 执行 WHERE 子句(删除元组,这称为选择)
  3. 根据 GROUP BY 对元组的子集进行聚类。
  4. 对于这些元组的每个集群:
  • 如果有 HAVING,则执行此谓词(类似于选择 WHERE)。请注意,您可以访问聚合函数。
  1. 对于这些元组的每个集群,只输出一个元组,使得:
    • 执行查询的 SELECT 部分(类似于上面聚合中的选择,即您可以使用聚合函数)。

窗口函数发生在 SELECT 阶段(它们考虑了该阶段 select 将输出的元组集)。

还有一个问题:

如果你有

select distinct ...

然后在所有其他事情完成后,从结果中删除重复的元组(即返回一组元组,而不是列表)。

最后,执行 ORDER BY。一旦 SELECT 部分完成,ORDER BY 在所有情况下都会发生在最后。

关于 JOINS。正如我上面提到的,它们发生在概念执行的“FROM”部分。 WHERE、GROUP BY、SELECT 适用于这些操作的结果。因此,您可以将这些视为执行查询的第一阶段。如果它包含子查询,则该过程是递归的。

顺便说一下,您可以在内部查询中引用内部查询的外部上下文中的关系,但反过来不行。

所有这些都是概念性的。实际上,为了提高效率,DBMS 可能会重写您的查询。

例如,假设 R(a,b) 和 S(a,c)。其中 S(a) 是引用 R(A) 的外键。

查询:

select b from R JOIN S using (a) where a > 10

可以被 DBMS 改写成类似这样的内容:

select b FROM R JOIN (select a from s where a > 10) as T using (a);

或:

select b FROM (select * from R where a > 10) as T JOIN S using (a);

事实上,DBMS 一直都在这样做。它接受您的查询,并创建备用查询。然后估计每个查询的执行时间并决定哪一个最有可能是最快的。然后它执行它。

这是查询评估的基本过程。请注意,这 3 个查询在结果方面是相同的。但是根据关系的大小,它们的执行时间可能会有很大不同。例如,如果 R 和 S 很大,但很少有 a>0 的元组,则连接浪费时间。如果子选择匹配很少的元组,每个带有子选择的查询可能会执行得很快,但如果它们匹配很多元组,就会很糟糕。这是发生在 DBMS 的查询评估引擎中的“魔法”类型。

,

您将执行顺序与 Logical Query Processing 混淆了。

我在谷歌上进行了快速搜索,发现了一堆将逻辑查询处理称为“执行顺序”的文章。让我们把这件事弄清楚。

逻辑查询处理

逻辑查询处理详细介绍了 SQL 查询的底层处理阶段...首先评估 WHERE 子句以便优化器知道从哪里获取数据,然后是表运算符等。

了解这一点将有助于您更好地设计和调整查询。逻辑查询处理顺序将帮助您理解为什么您可以在 ORDER BY 子句中通过列的别名引用列,而不能在其他任何地方引用。

enter image description here

执行顺序

考虑这个 WHERE 子句:

WHERE t1.Col1 = 'X'
 AND  t2.Col2 = 1
 AND  t3.Col3 > t2.Col4

优化器不需要以任何顺序评估这些谓词;它可以先评估 t2.Col2 = 1,然后 t1.Col1 = 'X'.... 优化器在某些情况下可以评估连接的顺序与您在查询中显示的顺序不同。当谓词逻辑规定结果将相同时,可以自由地做出(它认为的)最佳选择以获得最佳性能。

遗憾的是,关于这个话题的内容并不多。我确实多讨论了一点here

,

首先是 SQL 查询和适用于它的 SQL 规则。这就是其他答案中称为“逻辑查询处理”的内容。使用 SQL 指定结果。 SQL 标准不允许您指定如何达到此结果。

然后是查询优化器。根据统计、启发式、可用CPU数量、内存等因素,确定执行计划。它将评估执行预计需要多长时间。它将评估不同的执行计划以找到执行速度最快的一个。在这个过程中,它可以评估使用不同索引的执行计划,和/或重新排列连接顺序,和/或省略(外部)连接等。优化器有很多技巧。预计最佳执行计划的成本越高,评估的(高级)执行计划就越多。最终结果是一个(串行)执行计划和一个可能的并行执行计划。

所有评估的执行计划将保证正确的结果;根据“逻辑查询处理”匹配执行的结果。

最后是 SQL Server 引擎。在选择串行或并行执行计划后,它将执行它。

,

其他答案虽然包含有用和有趣的信息,但在我看来可能会引起混淆。

它们似乎都引入了“逻辑”执行顺序的概念,这与实际的执行顺序不同,好像这是 SQL 的特殊之处。

如果有人问到除 SQL 之外的任何普通语言的执行顺序,答案将是“严格顺序”或(对于表达式)“按照该语言的规则”。我觉得我们不会对编译器如何完全自由地重新排列和重新设计程序员编写的任何算法,并将其与源代码中的“逻辑”表示区分开来进行冗长的探索。>

最终,SQL 具有定义的求值顺序。这是其他答案中提到的“逻辑”顺序。最让新手困惑的是,这个顺序与SQL语句中子句的句法顺序不一致。

也就是说,一个简单的 SELECT...FROM...WHERE...ORDER BY 查询实际上是通过获取 from 子句中引用的表,根据 where 子句过滤行,然后操作列(包括过滤、重命名或生成列)根据 select-clause,最后根据 order-by-clause 对行进行排序。所以这里的子句被计算为第二、第三、第一、第四,这对任何明智的程序员来说都是一种无序的模式——SQL 的设计者更喜欢让它更符合普通英语口语的结构(“告诉我登记册上的姓氏!”)。

然而,当程序员编写 SQL 时,他们指定了产生结果的规范方法,就像他们用任何其他语言编写源代码一样。

数据库引擎执行的查询简化和优化(就像普通编译器执行的那样)将是一个完全独立的讨论主题,如果它还没有被混为一谈的话。这方面情况的本质是,数据库引擎可以对您提交的 SQL 做任何它该死的事情,前提是它返回给您的数据与就好像一样 SQL 中定义的求值顺序。

例如,它可以先对结果进行排序,然后对其进行过滤,尽管这种操作顺序与在 SQL 中评估相关子句的顺序明显不同。它可以做到这一点,因为如果你(比如)有一副随机顺序的牌,并通过牌组并扔掉所有的 A,然后将牌组按标准顺序排序,结果(就最终内容和牌组的顺序)与首先将牌组按标准顺序排序,然后通过并扔掉所有 A 没有什么不同。但是这种行为的全部细节和理由完全是一个单独的问题。