1

SQL Server 2008 R2 での更新トリガーに小さな問題があります。変更をログ テーブルに記録するトリガーをインストールしました。これは私にとってはうまくいきますが、複数の行を更新するときはうまくいきません。

CREATE TRIGGER [dbo].[TR_CompaniesUpdated]
ON  [dbo].[Companies]
AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;
DECLARE
    @Return NVARCHAR(MAX), -- Is the JSON of the data
    @ParameterSQL VARCHAR(MAX), -- Is the select we want to retreive in JSON format

    -- Data from of the row updated
    @ID INT,
    @StatusID SMALLINT,

    -- Old data from of the row
    @StatusID2 SMALLINT

-- Declare the cursor
DECLARE InsertedCursor CURSOR FAST_FORWARD FOR
SELECT [ID], [StatusID] FROM INSERTED

-- Create the temporary table with logs so we'll need to do only one insert after this
CREATE TABLE #tempLog(
    [ID] [int] NOT NULL,
    [Data] [nvarchar](max) NOT NULL,
    [ActionID] [int] NOT NULL,
    [DtCreated] [datetime] NOT NULL,
    [CreatedBy] [int] NOT NULL)

-- Save updated values into variables
OPEN InsertedCursor
FETCH NEXT FROM InsertedCursor
INTO @ID, @StatusID

WHILE @@fetch_status = 0
BEGIN
    -- Save old values into variables
    SELECT
        @StatusID2 = [StatusID]
    FROM DELETED
    WHERE [ID] = @ID

    SELECT * INTO #tempTable
    FROM DELETED
    WHERE [ID] = @ID

    -- Build the select only to et the changed data
    SET @ParameterSQL = 'SELECT '
    IF (ISNULL(@StatusID, '') != ISNULL(@StatusID2, '')) SET @ParameterSQL = @ParameterSQL + '[StatusID],'
    IF (@ParameterSQL != 'SELECT ')
    BEGIN
        -- Update the modified date
        UPDATE [dbo].[AssessedLevelCostCentre]
        SET [DtModified] = GETDATE()
        WHERE [ID] = @ID

        SET @ParameterSQL = SUBSTRING(@ParameterSQL, 0, LEN(@ParameterSQL)) + ' FROM #tempTable'

        -- Execute the JSON function to get the result in JSON format
        EXEC [dbo].[GetJSON] @ParameterSQL, @Return = @Return OUTPUT

        -- Insert the Change Log with the old data
        INSERT INTO #tempLog
        SELECT
            [ID] AS [ID],
            @Return AS [Data],
            [ActionID] AS [ActionID],
            CASE
                WHEN [DtModified] IS NULL
                    THEN [DtCreated]
                    ELSE [DtModified]
            END AS [DtModified],
            CASE
                WHEN [ModifiedBy] IS NULL
                    THEN [CreatedBy]
                    ELSE [ModifiedBy]
            END AS [CreatedBy]
        FROM #tempTable
    END
    ELSE
    BEGIN
        UPDATE [dbo].[AssessedLevelCostCentre]
        SET [ModifiedBy] = (SELECT [ModifiedBy] FROM #tempTable)
        WHERE [ID] = @ID
    END

    DROP TABLE #tempTable

    -- Advance the Cursor
    FETCH NEXT FROM InsertedCursor
    INTO @ID, @StatusID
END

CLOSE InsertedCursor
DEALLOCATE InsertedCursor

-- Insert the Change Log with the old data
INSERT INTO [dbo].[AssessedLevelsCostCentersLogs]
SELECT *
FROM #tempLog

DROP TABLE #tempLog
END

ご覧のとおり、JSON データを返すストアド プロシージャがあります。問題は、ストアド プロシージャがクエリからデータを取得し、DELETED または INSERTED テーブルを渡すことができないことです。これが一時テーブルを使用した理由です。トリックを行うようですが、ID句を追加します。

更新しました

コードを更新したところ、カーソルができましたが、ご覧のとおり、これのパフォーマンスは非常に遅いです。JSONを作成するためにアプリケーションを変更できないため、この方法でこれが必要になります。このようにトリガーするとうまくいくと思います。これのパフォーマンスを向上させるアイデアや方法はありますか?

4

1 に答える 1

3

Sql サーバーのトリガーはバッチごとに 1 回実行されるため、1 つのステートメントに 5000 レコードを挿入すると、トリガーは 1 回だけ開始されます。あなたが行ったことは、もちろん5000ではなく1つの値しか保持できないスカラー変数に値を設定することにより、個々のレコードごとに実行されると想定しています。挿入および削除への結合を利用するには、プロセスを再考する必要があります一度に 1 つのレコードを処理しません。

于 2012-06-12T20:28:02.173 に答える