2016-12-16 60 views
0

爲什麼在這段代碼中@changed是正確的,但@changed2是不正確的?目的是找出列值是否在更新語句中發生變化。我在這件事上沒有發現任何可以解釋這一點的細節。SQL Server UPDATE並同時設置變量操作順序

如果我不得不猜測,SQL Server將首先使用行的當前狀態更新設置變量,當它完成後它將實際更新它。這就是爲什麼@ changed2爲0,當解析@changed2變量時,VALUE列未被更新,所以我們必須使用@newValue。

IF Object_id('tempdb..#tmpTest') IS NOT NULL 
    DROP TABLE #tmptest; 

CREATE TABLE #tmptest 
    ( 
    test_id INT NOT NULL IDENTITY(1, 1), 
    value VARCHAR(20) 
); 

INSERT INTO #tmptest 
      (value) 
VALUES  ('hello'); 

然後運行該代碼多次(可以運行了好幾次,但不再次執行上述代碼!):

--then run this multiple times: 
DECLARE @oldValue VARCHAR(20), 
     @newValue VARCHAR(20) = (SELECT value 
      FROM #tmptest 
      WHERE test_id = 1), 
     @changed BIT, 
     @changed2 BIT 


--alternate values: 
IF(@newValue = 'hello') 
    SET @newValue = 'goodbye' 
ELSE 
    SET @newValue = 'hello' 

--see current data 
SELECT * 
FROM #tmptest; 

--update 
UPDATE #tmptest 
SET value = @newValue, 
     @oldValue = value, 
     @changed = CASE 
        WHEN @oldValue != @newValue THEN 1 
        ELSE 0 
        END, 
     @changed2 = CASE 
        WHEN @oldValue != value THEN 1 
        ELSE 0 
        END 
WHERE test_id = 1; 

--see changed data: 
SELECT * 
FROM #tmptest; 

SELECT @oldValue oldValue, 
     @newValue newValue, 
     @changed changed, 
     @changed2 changed2 

謝謝您的時間。

+2

該代碼的結果實際上是未定義的;混合列分配與變量分配意味着所有的投注都關閉。搜索「古怪的更新」以獲取更多信息(以及爲什麼人們有時會使用這個)。 –

+0

這是不正確的,甚至微軟推薦它在模擬SQL 2008 R2的序列時,像設置@NewSeqVal = CurrVal = CurrVal + Incr這裏鏈接:https://blogs.msdn.microsoft.com/sqlcat/2006/04/10/sql -server-sequence-number/ – Alex

+0

好吧,我發現他們甚至[更新了文檔](https://msdn.microsoft.com/library/ms177523)來編譯這種更新形式。同樣的文檔也解釋了你所看到的:「SET @variable = column = expression'將變量設置爲與列相同的值,這不同於'SET @variable = column,column = expression',它設置變量到列的更新前的值。「 –

回答

1

執行更新時,如果需要知道原始記錄上的值,SQL Server將在任何更新到位之前讀取原始數據。因此,在你的榜樣,這一進程將是這樣的:

Read value as @OldValue 
Compare @oldValue and value 
    Set @changed 
    Set @changed2 
Update value from @newValue 

在涉及讀取,更新,並在一個命令返回更新前的值,防止其他進程的某些代碼這種行爲實際上是算的上檢索相同的值。