1

テーブル変数を選択するストアド プロシージャ内で奇妙なことがわかりました。カーソルの最初の反復でフェッチされた値を (後続の反復で) 常に返します。これを証明するサンプルコードを次に示します。

DECLARE @id AS INT;
DECLARE @outid AS INT;

DECLARE sub_cursor CURSOR FAST_FORWARD
    FOR SELECT [TestColumn]
        FROM   testtable1;

OPEN sub_cursor;

FETCH NEXT FROM sub_cursor INTO @id;

WHILE @@FETCH_STATUS = 0
BEGIN            
        DECLARE @Log TABLE (LogId BIGINT NOT NULL);
        PRINT 'id: ' + CONVERT (VARCHAR (10), @id);

        INSERT INTO Testtable2 (TestColumn)
        OUTPUT inserted.[TestColumn] INTO @Log
        VALUES (@id);

        IF @@ERROR = 0
        BEGIN
                SELECT TOP 1 @outid = LogId
                FROM   @Log;
                PRINT 'Outid: ' + CONVERT (VARCHAR (10), @outid);

                INSERT INTO [dbo].[TestTable3] ([TestColumn])
                VALUES (@outid);
        END
        FETCH NEXT FROM sub_cursor INTO @id;
    END

CLOSE sub_cursor;

DEALLOCATE sub_cursor;

ただし、SOにコードを投稿してさまざまな組み合わせを試しているときに、下の行からトップを削除すると、カーソル内のテーブル変数から正しい値が得られることがわかりました。

SELECT TOP 1 @outid = LogId FROM   @Log;

これはこのようになります

SELECT @outid = LogId FROM   @Log;

ここで何が起こっているのかわかりません。ループの繰り返しごとに新しいテーブルが作成されると考えて、テーブル変数の TOP 1 が機能するはずだと思いました。誰かがテーブル変数のスコープと有効期間に光を当てることができますか?

更新:ここで奇妙な動作を回避する解決策があります。解決策として、ループの前にテーブルを宣言し、ループの先頭ですべての行を削除しました。

4

1 に答える 1

4

このコードには多くの点があります。

まず、エラーが発生した場合は組み込みトランザクションをロールバックしますが、成功した場合にコミットすることはありません。書かれているように、これはトランザクションをリークし、次のコードで大きな問題を引き起こす可能性があります.

テーブルの状況について混乱を招く可能性があるの@Logは、SQL Server が C++ やその他の標準プログラミング言語と同じ変数のスコープと有効期間の規則を使用していないことです。カーソル ブロックでテーブル変数を宣言する場合でも@Log、バッチの残りの期間存続し、複数の行が挿入される 1 つのテーブルのみが取得されます。

その結果、テーブルに決定論的な順序付けを課す句がないため、 の使用TOP 1はあまり意味がありません。ORDER BYそれがなければ、SQL Server が適切と考える順序を取得します。この場合は挿入順序のように見え、SELECT.

最後の ID 値のみが本当に必要な場合は、@Logテーブルに実際の順序付け基準を提供する必要があります。データ列の横に自動番号付けまたは日付フィールドの形式を指定して、目的の順序を適切に指定するために使用できます。 .

于 2012-06-19T14:05:17.900 に答える