2012-08-01 18 views
1

我正在編寫一個存儲過程,以基於表變量參數更新多個記錄。從表變量更新表中的多行

現有的表是:Tb_Project_Image與相關列:

id PK (identity 1,1) 
cat_ord decimal(4,2) 

程序將收到包含id作爲PI_ID臨時表變量(在下面的代碼示出),以及用於cat_ord新值如newCatOrdidx是包含1 ... n的每一行的簡單標識,其中n是@tempTable的行數。

對於@tempTable中的每一行,我想更新Tb_Project_Image,其中id = PI_ID爲相應的值。現在

DECLARE @tempTable table (
    idx smallint Primary Key IDENTITY(1,1), 
    PI_ID bigint, 
    newCatOrd decimal(4, 2) not null) 

    INSERT INTO @tempTable values (3, 7.01) 
    INSERT INTO @tempTable values (4, 7.02) 
    INSERT INTO @tempTable values (5, 7.03) 
    --etc... 
    DECLARE @error int 
    DECLARE @update int 
    DECLARE @iter int 
    SET @iter = 1 

    BEGIN TRAN 
WHILE @iter <= (select COUNT(*) from @tempTable) 
    BEGIN 
     UPDATE Tb_Project_Image 
     SET cat_ord = (SELECT newCatOrd FROM @tempTable 
         WHERE idx = @iter) 
     WHERE id = (SELECT PI_ID FROM @tempTable 
         WHERE idx = @iter) 
     --error checking    
     set @error = @@ERROR 
     set @update = @@ROWCOUNT 
     IF ((@error = 0) AND (@update = 1)) 
      BEGIN 
      SET @iter = @iter + 1 
      CONTINUE 
      END 
     ELSE 
      BREAK 
    END 

IF ((@error = 0) AND (@update = 1)) 
    COMMIT TRAN 
ELSE 
    ROLLBACK TRAN 
GO 

,錯誤檢查是因爲,爲了保證完整性,臨時表中的每一行必須做出1次更新。 (爲了節省空間,省略瞭解釋)如果while循環的一次迭代拋出一個錯誤,或者不完全影響一行,我想打破循環並回滾事務

我遇到的問題是此錯誤檢查不起作用。我目前使用@tempTable中的14行運行它,第11次使用Project_Image表中找不到的PI_ID。因此,@update = 0...但它繼續循環並提交數據。

如果有人有這樣做的方法只使用一個更新語句,我會非常高興。

回答

3

你不能這樣做,因爲即使SET重置@@ ERROR和@@ ROWNUMBER變量的狀態。在這種情況下,set @error = @@ERROR之後@@ ROWCOUNT被設置爲1。如果不將值賦給局部變量,你的代碼將工作:

IF ((@@error = 0) AND (@@rowcount = 1)) 

但是,你可能寧願更新後分別嘗試try...catch error handling和測試@@ ROWCOUNT。

UPDATE:在單個更新這樣做:

UPDATE t 
    SET cat_ord = tt.newCatOrd 
    FROM Tb_Project_Image t 
INNER JOIN @tempTable tt 
    ON t.id = tt.PI_ID 
-- If there was PI_ID not found in Tb_Project_Image 
-- But I think that this should have been dealt with 
-- During the initial loading of temporary table 
IF @@ROWCOUNT <> (select count (*) from @tempTable) 
BEGIN 
    -- Error reporting here 
    ROLLBACK TRANSACTION 
END 
+0

您也可以將@@ ERROR和@@ ROWNCOUNT設置爲變量,如果您在單個語句中執行該操作...'SELECT @error = @@ ERROR,@update = @@ ROWCOUNT' – 2012-08-02 05:15:39

+0

這樣工作就像一個魅力, 謝謝。是的,這個安全網可能永遠不會被絆倒,但我喜歡謹慎。我主要使用回滾,因爲我在最初的代碼中有多個更新。感謝您向我展示一種方式,以便一次完成所有工作。 – 2012-08-02 15:38:53

+0

@LoganDangerBlack不客氣:-) – 2012-08-02 18:05:46

0

而不是更新的,然後回滾,你也可以使用一個CTE來確定是否有任何記錄之前應執行更新進行更新。像這樣的東西應該工作:

WITH NON_SINGLETON AS (
    -- Find any records in @tempTable that don't match 
    -- exactly one record in Tb_Project_Image 
    SELECT t.PI_ID, COUNT(pi.id) C 
    FROM @tempTable t 
    LEFT JOIN Tb_Project_Image pi ON t.PI_ID = pi.id 
    GROUP BY t.PI_ID 
    HAVING COUNT(pi.id) != 1 
) 
UPDATE Tb_Project_Image 
SET cat_ord = t.newCatOrd 
FROM Tb_Project_Image pi 
JOIN @tempTable t ON pi.id = t.PI_ID 
-- If any invalid records were found in the CTE, 
-- then this condition will fail for all rows 
-- and nothing will be updated 
WHERE NOT EXISTS(SELECT 1 FROM NON_SINGLETON) 

如果有可能爲@tempTable有重複項爲同一PI_ID,那麼這將處理這些情況也是如此。而且由於這是一個單一的聲明,所以您不必在proc中顯式管理事務(如果它是唯一需要包含在事務中的事務)。