SQL查询中的执行顺序?

时间:2010-07-17 09:59:24

标签: sql-server-2005 tsql

以下查询是一个人为的例子,演示了我在本周存储过程中发现的错误。

CREATE TABLE #temp
(
    ID int IDENTITY(1,1),
    Value char(1)
)

INSERT INTO #temp(Value) Values('a')
INSERT INTO #temp(Value) Values('b')
INSERT INTO #temp(Value) Values('c')
INSERT INTO #temp(Value) Values('d')

DECLARE
    @i int,
    @ID int,
    @Count int,
    @Value char(1)

SELECT @Count = COUNT(*) FROM #temp
SET @i = 1
SET @ID = 2

WHILE @i < @Count
BEGIN
    SELECT 
        @ID = ID,
        @Value = (SELECT Value FROM #temp WHERE ID = @ID)
    FROM
        #temp
    WHERE
        @i = ID

    PRINT @Value

    SET @i = @i + 1
END

乍一看输出应该是a b c d,但事实并非如此!它是b b c d。因此,声明中的执行顺序不是我们可能认为的那样。

是否有可以依赖的特定执行顺序?

6 个答案:

答案 0 :(得分:4)

中的WHERE子句
@Value = (SELECT Value FROM #temp WHERE ID = @ID)

与此处的WHERE子句不相关

    #temp
WHERE
    @i = ID

所以,第一次循环

  • @i = 1,@ ID = 2
  • 在#temp中,@ ID = 2,所以你得到b
  • 然后指定@ID为1

第二次循环

  • @i = 2,@ ID = 1
  • 在#temp中,@ ID = 1,因此您获得a
  • 然后指定@ID为2

第3次循环

  • @i = 3,@ ID = 2
  • 在#temp中,@ ID = 2,所以你得到b
  • 然后为@ID
  • 指定@ID

然后由于@i < @count

而停止

SQL没有“执行顺序”,因为它是声明性的。 SELECT子句一气呵成,没有期望@ID将在下一行使用之前被分配。

答案 1 :(得分:2)

我很惊讶它在你的系统上打印了四个字母,@i < @Count应该将输出限制为3行。

您通常可以依赖按顺序评估的列分配。但是子查询可以按不同的顺序进行评估(甚至可以转换为连接以提高效率。)如果使用子查询将分配与查询分开:

SELECT  @ID = ID
,       @Value = TheVal
FROM    (
        SELECT  ID
        ,       (SELECT Value FROM #temp WHERE ID = @ID) as TheVal
        FROM    #temp
        WHERE   @i = ID
        ) sub

结果应该是可靠的。在这里,它是b a b,因为它以@id = 2开头,然后在更新TheVal值之后每个循环增加它。

依靠这种技巧会使代码难以维护。它有时可能是性能所必需的,但如果不是,请尝试写清楚:)

答案 2 :(得分:1)

在SQL中,出现在同一逻辑查询处理阶段的所有表达式都被评估为在同一时间点。

我的意思是,我们不能说首先评估@ID= ID,然后再评估

@Value = (SELECT Value FROM #temp WHERE ID = @ID).

此外,条款的执行顺序为:

FROM

在哪里

GROUP BY

HAVING

选择

ORDER BY

这就是SELECT子句的别名只能在ORDER BY子句中引用的原因。

干杯。

答案 3 :(得分:1)

让我们仔细看看您的问题:

  1. 第一次运行:

    选择         @ID = ID,         @Value =(SELECT值FROM #temp WHERE ID = @ID)     从         #TEMP     哪里         @i = ID

  2. 这里

     @i=1
       @ID=2
    

    所以

    之后
     @Value = (SELECT Value FROM #temp WHERE ID = @ID)
    

    我们得到:

     @Value = 'b'
    

    并且由于@ ID = ID,@ ID值将等于1,因为ID = 1是

    之后唯一可用的值
      

    WHERE               @i = ID

    已执行。

    1. 第二轮

      @ I = 2 @ ID = 1

    2. 此次运行将打印

        

      'A'

      3 。第三次运行     @ ID = 3    @ ID = 2

      此次运行将打印

        

      'B'

      然后循环结束!

      这就是为什么你得到'b'''b'

答案 4 :(得分:1)

实际上,正如我最近发现的那样,有一个逻辑处理顺序(虽然不是保证的物理执行顺序 - 因为它基于查询处理器)

参考:http://msdn.microsoft.com/en-us/library/ms189499.aspx

  

SELECT的逻辑处理顺序   声明

     

以下步骤显示逻辑   处理订单或绑定订单,   对于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.   

答案 5 :(得分:0)

不确定您要实现的目标,以及如何获得结果。

我将你的代码粘贴到MSSQL 2008中,然后得到了

  

b   一个   B'/ P>

首先,你有一个“小于计数”的比较,结果只有3条记录。

接下来,您有两个相互矛盾的WHERE子句,一个带ID,另一个带i。 SELECT更新ID。

删除子选择,然后更改为&lt; = @Count,你应该得到“a b c d”