SQL查询的执行顺序

时间:2011-01-04 17:20:32

标签: sql-server

我对此查询的执行顺序感到困惑,请解释一下。 我很困惑何时应用连接,调用函数,随Case添加新列以及添加序列号时。请解释所有这些的执行顺序。

select Row_number() OVER(ORDER BY (SELECT 1))  AS 'Serial Number', 
    EP.FirstName,Ep.LastName,[dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole) as RoleName,  
    (select top 1 convert(varchar(10),eventDate,103)from [3rdi_EventDates] where EventId=13) as EventDate,
    (CASE [dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole)  
            WHEN    '90 Day Client' THEN 'DC'
            WHEN    'Association Client'  THEN  'DC'
            WHEN    'Autism Whisperer'    THEN  'DC'
            WHEN    'CampII'             THEN   'AD' 
            WHEN    'Captain'              THEN 'AD' 
            WHEN    'Chiropractic Assistant' THEN 'AD'
            WHEN    'Coaches'               THEN 'AD'
            END) as Category from [3rdi_EventParticipants] as EP  
    inner join [3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId  
    where EP.EventId = 13
    and userid in (  
    select distinct userid from userroles  
    --where roleid not in(6,7,61,64) and roleid not in(1,2))  
    where roleid not in(19, 20, 21, 22) and roleid not in(1,2))

这是从上述查询中调用的函数。

CREATE function [dbo].[GetBookingRoleName]  
(  
 @UserId as integer,
 @BookingId as integer
)  
RETURNS varchar(20)  
as  
begin  
declare @RoleName varchar(20)  

if @BookingId = -1
Select Top 1 @RoleName=R.RoleName From UserRoles UR inner join Roles R on UR.RoleId=R.RoleId Where UR.UserId=@UserId and R.RoleId not in(1,2)  
else
Select @RoleName= RoleName From Roles where RoleId = @BookingId

return @RoleName  
end

6 个答案:

答案 0 :(得分:42)

SQL没有执行顺序。是一种声明性语言。优化器可以自由选择任何合适的顺序来产生最佳执行时间。给定任何SQL查询,基本上不可能任何人假装它知道执行顺序。如果您添加有关所涉及的模式的详细信息(确切的表和索引定义)和估计的基数(数据的大小和键的选择性),那么可以在可能的执行顺序中进行 guess

最终,唯一正确的'顺序'是实际执行计划中描述的顺序。请参阅Displaying Execution Plans by Using SQL Server Profiler Event ClassesDisplaying Graphical Execution Plans (SQL Server Management Studio)

但完全不同的是,查询,子查询和表达式如何将自己投射到“有效性”中。例如,如果SELECT投影列表中有别名表达式,是否可以在WHERE子句中使用别名?像这样:

SELECT a+b as c
FROM t
WHERE c=...;

在where子句中使用c别名是否有效?答案是不。查询形成语法树,树的下部分支不能引用树中更高的定义。这不一定是“执行”的顺序,更多的是语法分析问题。它等同于在C#中编写此代码:

void Select (int a, int b)
{
   if (c = ...) then {...}
   int c = a+b;
}

就像在C#中一样,这个代码不能编译,因为在定义之前使用了变量c,上面的SELECT将无法正确编译,因为别名c在树中被引用的低于实际上是定义了。

不幸的是,与众所周知的C / C#语言解析规则不同,构建查询树的SQL规则在某种程度上是深奥的。在Single SQL Statement Processing中简要提及了它们,但详细讨论了它们是如何创建的,以及什么顺序是有效的,什么不是,我不知道任何来源。我不是说没有好的资料来源,我确信那些好的SQL书籍都涵盖了这个主题。

请注意,语法树顺序与SQL文本的可视顺序不匹配。例如,ORDER BY子句通常是SQL文本中的最后一个,但作为语法树,它位于其他所有内容之上(它对SELECT的输出进行排序,因此它位于SELECTed列的上方,因此因此 有效引用c别名:

SELECT a+b as c
FROM t
ORDER BY c;

更新

实际上有:Logical Processing Order of the SELECT statement

  

以下步骤显示逻辑   处理订单或绑定订单,   对于SELECT语句。这个命令   确定何时定义对象   一步到位了   后续步骤中的条款。对于   例如,如果查询处理器可以   绑定(访问)表或视图   这些在FROM子句中定义   对象及其列已制作完成   可用于所有后续步骤。   相反,因为SELECT子句   是第8步,任何列别名或   该子句中定义的派生列   不能通过前面引用   条款。但是,他们可以   由后续条款引用   作为ORDER BY子句。请注意   实际的物理执行   语句由查询确定   处理器和订单可能会有所不同   这个清单。

     
      
  1. FROM
  2.   
  3. ON
  4.   
  5. JOIN
  6.   
  7. WHERE
  8.   
  9. GROUP BY
  10.   
  11. WITH CUBE或WITH ROLLUP
  12.   
  13. HAVING
  14.   
  15. 选择
  16.   
  17. DISTINCT
  18.   
  19. ORDER BY
  20.   
  21. TOP
  22.   

答案 1 :(得分:39)

通常按以下顺序(SQL Server)处理查询。我不知道其他RDBMS是否这样做。

FROM [MyTable]
    ON [MyCondition]
  JOIN [MyJoinedTable]
 WHERE [...]
 GROUP BY [...]
HAVING [...]
SELECT [...]
 ORDER BY [...]

答案 2 :(得分:5)

SQL是一种声明性语言,这意味着它告诉SQL引擎要做什么,而不是如何做。这与诸如C之类的命令式语言形成鲜明对比,其中明确规定了如何做某事。

这意味着并非所有语句都会按预期执行。特别值得注意的是布尔表达式,它们可能无法从左到右进行评估。例如,不保证以下代码在没有除零错误的情况下执行:

SELECT 'null' WHERE 1 = 1 OR 1 / 0 = 0

原因是查询优化器选择最佳(最有效)的方式来执行语句。这意味着,例如,可以在应用变换谓词之前加载和过滤值,从而导致错误。请参阅上面的第二个链接以获取示例

请参阅:herehere

答案 3 :(得分:2)

“执行顺序”可能是SQL查询的错误心智模型。很难实际编写一个实际依赖于执行顺序的查询(这是一件好事)。相反,你应该考虑所有连接和where子句同时发生(几乎像模板)

那说你可以显示Execution Plans,这可以让你深入了解它。

然而,由于不清楚为什么你想知道执行的顺序,我猜你试图获得这个查询的心理模型,所以你可以用某种方式修复它。这就是我将“翻译”你的查询的方式,虽然我已经很好地处理了这种分析,但它有一些灰色区域,它的精确程度。

FROM AND WHERE CLAUSE

  • 向我提供所有活动参与者的行。 from [3rdi_EventParticipants

  • 同时向我提供与SignUpID inner join 3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId上的事件参与者行匹配的所有事件注册行

  • 但仅适用于事件13 EP.EventId = 13

  • 并且仅当用户标识在用户角色表中有一个记录,其中角色id不在1,2,19,20,21,22中 userid in (
    select distinct userid from userroles
    --where roleid not in(6,7,61,64) and roleid not in(1,2))
    where roleid not in(19, 20, 21, 22) and roleid not in(1,2))

SELECT CLAUSE

  • 对于每一行,都会给我一个唯一的ID Row_number() OVER(ORDER BY (SELECT 1)) AS 'Serial Number',

  • 参与者名字EP.FirstName

  • 参与者姓氏Ep.LastName

  • 预订角色名称GetBookingRoleName

  • 查看活动日期,找出您发现EventId = 13的第一个eventDate (select top 1 convert(varchar(10),eventDate,103)from [3rdi_EventDates] where EventId=13) as EventDate

  • 最后翻译类别中的GetBookingRoleName。我没有这方面的表格,所以我会手动映射(CASE [dbo].[GetBookingRoleName](ES.UserId,EP.BookingRole)
    WHEN '90 Day Client' THEN 'DC' WHEN 'Association Client' THEN 'DC' WHEN 'Autism Whisperer' THEN 'DC' WHEN 'CampII' THEN 'AD' WHEN 'Captain' THEN 'AD' WHEN 'Chiropractic Assistant' THEN 'AD' WHEN 'Coaches' THEN 'AD' END) as Category

这里有几点说明。当您选择TOP时,您没有任何订购。你可能应该在那里订购。您也可以轻松地将其添加到from子句中,例如:

from [3rdi_EventParticipants] as EP  
    inner join [3rdi_EventSignup] as ES on EP.SignUpId = ES.SignUpId,   
       (select top 1 convert(varchar(10),eventDate,103)
       from [3rdi_EventDates] where EventId=13
       Order by eventDate) dates

答案 4 :(得分:1)

有一个逻辑顺序可以评估查询文本,但是数据库引擎可以根据最佳条件来选择执行查询组件的顺序。下面列出了逻辑文本解析顺序。例如,这就是为什么您不能在SELECT子句中使用WHERE子句的别名。就查询解析过程而言,别名尚不存在。

  1. FROM

  2. 打开

  3. 外部

  4. 位置

  5. CUBE |卷起

  6. 爱好

  7. 选择

  8. DISTINCT

  9. 订购

  10. TOP

有关更多信息,请参见the Microsoft documentation (see "Logical Processing Order of the SELECT statement")

答案 5 :(得分:1)

SQL查询不是强制性的,而是声明性的,因此您不知道首先执行哪个语句,但是由于SQL是由SQL查询引擎评估的,因此大多数SQL引擎都遵循类似的过程来获取结果。您可能必须了解查询引擎如何在内部工作以了解某些SQL执行行为。

朱莉娅·埃文斯(Julia Evens)的一篇精彩文章对此进行了解释,值得一看:

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

enter image description here

相关问题