1

テーブル変数パラメーターに基づいて複数のレコードを更新するストアド プロシージャを作成しています。

既存のテーブルは次のとおりです:関連する列を持つTb_Project_Image :

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

このプロシージャは、id as とasPI_IDの新しい値を含む一時テーブル変数 (以下のコードに示す) を受け取ります。は、1...n を含む各行の単純な ID です。ここで、n は の行数です。cat_ordnewCatOrdidx@tempTable

の各行について、 whereを対応する値@tempTableに更新したいと思います。Tb_Project_Imageid = 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 回の反復でエラーがスローされた場合、または 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))

ただし、try...catch エラー処理を試して、更新後に @@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 に答える