2010-07-17 96 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