sp_executesql не объявляет переменную - PullRequest
0 голосов
/ 16 мая 2018

Я пытаюсь создать динамический SQL, например, для создания журнала аудита, который просто показывает изменения.

При запуске я получаю сообщение об ошибке

Необходимо объявить скалярную переменную "@OldValueOut".

Я думал, что объявил это через второй параметр вызова sp_executesql.

ALTER TRIGGER [dbo].[Lab_SubSpace_Contact_Changed] 
   ON [dbo].[Lab_SubSpace_Contact]
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @sql VARCHAR(4000),
            @sqlDeleted VARCHAR(4000),
            @sqlInserted NVARCHAR(4000),
            @columns NVARCHAR(4000),
            @item VARCHAR(80),
            @pos INT,
            @oldValue VARCHAR(100),
            @newValue VARCHAR(100),
            @tableName VARCHAR(80),
            @pkName VARCHAR(80);

    SET @tableName = 'Lab_SubSpace_Contact';
    SET @pkName = 'SubSpace_Id';

    SELECT * INTO #deleted FROM deleted;
    SELECT * INTO #inserted from inserted;

    SET @columns = STUFF((SELECT ',' + name
                          FROM sys.columns
                          WHERE object_id = OBJECT_ID(@tableName)
                            AND SUBSTRING(COLUMNS_UPDATED(), ((column_id - 1) / 8 + 1), 1) & (POWER(2, ((column_id - 1) % 8 + 1) - 1)) = POWER(2, (column_id - 1) % 8)
                          FOR XML PATH('')
                         ), 1, 1, '');

    WHILE LEN(@columns) > 0
    BEGIN
        SET @pos = CHARINDEX(',', @columns);
        IF @pos = 0
        BEGIN
            SET @item = @columns
        END;
        ELSE
        BEGIN
            SET @item = SUBSTRING(@columns, 1, @pos - 1);
        END;

        SET @sqlDeleted = N'SELECT @OldValueOut = ' + QUOTENAME(@item) + 'FROM #deleted WHERE ' + QUOTENAME(@pkName) + ' = ' + CONVERT(VARCHAR(100), @pkName);
        print @sqlDeleted;
        EXECUTE sp_executesql @sqlDeleted, N'@OldValueOut NVARCHAR(100) OUTPUT', @OldValueOut = @oldValue OUTPUT;

        SET @sqlInserted = N'SELECT @NewValueOut = ' + @item + ' FROM inserted WHERE ' + QUOTENAME(@pkName) + ' = ' + CONVERT(VARCHAR(100), @pkName);
        EXECUTE sp_executesql @sqlDeleted, N'@NewValueOut NVARCHAR(100) OUTPUT', @NewValueOut = @newValue OUTPUT;

        IF (LTRIM(RTRIM(@newValue)) != LTRIM(RTRIM(@oldValue)))
        BEGIN
            SET @sql = 'INSERT INTO [dbo].[Audit_Log] (Table_Name, Primary_Key, Column_Name, Old_Value, New_Value, Modified, Updated_By_WWID) VALUES (' +
                        QUOTENAME(@tableName, '''') + ',' +
                        QUOTENAME(@pkName, '''') + ',' + 
                        QUOTENAME(@item, '''') + ',' +
                        QUOTENAME(@oldValue, '''') + ',' +
                        QUOTENAME(@newValue, '''') + ',' +
                        GETDATE() + ',' +
                        '1234' + ')';
            EXEC(@sql);
        END;

        SET @item = '';
        SET @newValue = '';
        SET @oldValue = ''

        IF @pos = 0 
        BEGIN
            SET @columns = '';
        END;
        ELSE
        BEGIN
            SET @columns = SUBSTRING(@columns, @pos + 1, LEN(@columns) - @pos);
        END;
    END ;

    DROP TABLE #inserted;
    DROP TABLE #deleted;
END

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Упс. Мой второй sp_execute также ссылался на старое значение вместо нового значения. Спасибо всем.

0 голосов
/ 16 мая 2018

Я никогда не помню правильный способ заказа объявлений для переменных с sp_executesql.Итак, я просто изменяю и использую одно и то же имя переменной:

DECLARE @oldValueOut VARCHAR(100);

    SET @sqlDeleted = N'
SELECT @OldValueOut = ' + QUOTENAME(@item) + '
FROM #deleted 
WHERE ' + QUOTENAME(@pkName) + ' = ' + CONVERT(VARCHAR(100), @pkName);

EXECUTE sp_executesql @sqlDeleted, 
                      N'@OldValueOut NVARCHAR(100) OUTPUT',
                      @OldValueOut = @oldValueOut OUTPUT;

Здесь - упрощенная версия, показывающая, что это работает для @oldValueOut.

...