2010-07-17 94 views
0

以下查询是一个人为的示例,演示了本周在存储过程中发现的错误。SQL查询中的执行顺序?

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。所以声明中的执行顺序并不是我们可能认为的那样。

是否存在可依赖的特定执行顺序?

+0

你给出的查询的输出是B A B – Kashif 2010-07-17 10:25:22

+0

是的,你是对的,但因为每个人都在自己的答案已经发表了评论我现在不会改变。 :) – 2010-07-17 10:55:47

回答

4

的在当前行

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

条款是不相关的WHERE子句这里

#temp 
WHERE 
    @i = ID 

所以,第一环路

  • @i = 1,@ID = 2
  • 在#TEMP,@ID = 2,所以你得到b
  • 你然后用1

第二环分配@ID

  • @i = 2,@ID = 1
  • 在#TEMP,@ID = 1,所以你得到a
  • 然后你可以指定@ID 2

第三环

  • @i = 3,@ID = 2
  • 在#TEMP,@ID = 2,所以你得到b
  • 你然后用3

然后分配给它@ID因为@i < @count

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

1

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

我的意思是,我们不能说@ID= ID将先进行计算,然后

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

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

FROM

WHERE

GROUP BY

HAVING

选择

ORDER BY

这就是为什么在SELECT子句的别名只能在ORDER BY子句中引用。

干杯。

+0

那又如何?它只向表中插入数据。这里的问题是循环内的查询。 – 2010-07-17 10:17:18

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值之后每增加一个循环

依靠这种技巧使代码很难维护。它有时是必要的性能,但如果不是,尝试写为清楚起见:)

+0

你是对的,它不打印四个字母。我不在办公室,所以我只是从记忆中挖掘出一些东西。 – 2010-07-17 10:52:57

+0

另外,我没有写它!但我必须修复它...... – 2010-07-17 10:54:33

0

不知道你正在努力达到什么,以及你如何得到你的结果。

我粘贴代码为2008 MSSQL,并得到了

b 一个 b

首先,你有一个 “比数少” 的比较,导致只有3个记录。

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

删除子选择,并更改为< = @count,你应该得到 “ABCD”

+2

如果您阅读它所说的“人为的例子”这个问题。 – 2010-07-17 10:53:44

1

让我们来看看更接近你的查询:

  1. 第一次运行:

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

这里

@i=1 
    @ID=2 

打完

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

我们得到:

@Value = 'b' 

和@ID值相等的,因为1 @ ID = ID,因为ID = 1是01之后的唯一可用值

其中执行 @i = ID

  • 第二运行

    @ I = 2 @ ID = 1

  • 此运行将打印

    'A'

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

    此运行将打印

    'B'

    然后循环终止!

    这就是为什么你得到了“B”“A”“B”

    1

    其实,最近我发现,有一个逻辑处理订单(虽然不是保证物理执行命令 - 因为这是基于查询处理器)

    价:在SELECT语句 的http://msdn.microsoft.com/en-us/library/ms189499.aspx

    逻辑处理顺序

    以下步骤显示吨他为逻辑 处理订单或绑定订单, 为SELECT语句。此订单 确定在 一步中定义的对象何时可用于后续步骤中的 子句。对于 例如,如果查询处理器可以 结合(访问)的表或视图 在FROM子句定义的,这些 对象和它们的列是由 提供给所有的后续步骤。 相反,由于SELECT子句 是第8步,因此子句 中定义的任何列别名或 派生列都不能被前面的 子句引用。但是,它们可以是 作为ORDER BY子句在后续子句中引用,例如 。 请注意, 语句的实际物理执行是由查询 处理器确定的,并且订单可能与此列表中的 不同。 (重点煤矿)

    1. FROM
    2. ON
    3. JOIN
    4. WHERE
    5. GROUP BY
    6. WITH CUBE或WITH ROLLUP
    7. HAVING
    8. SELECT
    9. DISTINCT
    10. ORDER BY
    11. TOP