1

我正在编写一个存储过程来根据表变量参数更新多条记录。

现有表是:Tb_Project_Image与相关列:

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

该过程将接收一个临时表变量(如下面的代码所示),其中包含 id as和asPI_ID的新值。是包含 1...n 的每一行的简单标识,其中 n 是 的行数。cat_ordnewCatOrdidx@tempTable

对于 中的每一行@tempTable,我想将Tb_Project_Imagewhere更新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 循环的单次迭代引发错误,或者没有准确影响 1 行,我想中断循环并回滚事务

我遇到的问题是这个错误检查不起作用。我目前正在运行它,其中有 14 行,@tempTable第 11 行使用表PI_ID中未找到的Project_Image。因此,@update = 0...但它继续循环并提交数据。

如果有人有一种只使用单个更新语句的方法,我会倍加高兴。

4

2 回答 2

3

你不能这样做,因为即使是 SET 也会重置 @@ERROR 和 @@ROWNUMBER 变量的状态。在这种情况下,@@ROWCOUNT 在之后设置为 1 set @error = @@ERROR。如果您不将值分配给局部变量,您的代码将起作用:

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

但是您可能更愿意尝试尝试...捕获错误处理并在更新后单独测试@@rowcount。

更新:在单个更新中进行:

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
于 2012-08-01T22:54:05.987 回答
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 中显式管理事务(如果它是唯一需要包含在事务中的事物)。

于 2012-08-02T05:41:39.310 回答