Триггер только вставляя 1 символ переменной в таблицу - PullRequest
2 голосов
/ 25 апреля 2019

У меня есть следующий SQL-триггер, который вставляет данные в таблицу с именем PS_AUDIT_PSROLEUSR на основе действия «Удалить», связанного с связанной таблицей.

Когда обновление таблицы инициируется онлайн-пользователем вприложение (строка роли удаляется), тогда весь OPRID вставляется правильно в PS_AUDIT_PSROLEUSR, однако, когда триггеры запускаются через написанную мной пакетную программу, он получает только первую букву AUDIT_OPRID.Я подтвердил это, запустив программу под другим OPRID, и он все еще вставляет только первый символ в столбец.Простите, что я не очень хорошо знаком с триггерами SQL.

ALTER TRIGGER [dbo].[PSROLEUSER_TR] 
ON [dbo].[PSROLEUSER]
FOR INSERT, UPDATE, DELETE
AS
    SET NOCOUNT ON

    DECLARE @XTYPE CHAR(1), @OPRID CHAR(30)

    SET @OPRID = NULL

    SELECT @OPRID = CASE(CHARINDEX(',', CAST(context_info AS CHAR(128))))
                       WHEN 0 THEN 'Native SQL'
                       ELSE SUBSTRING(CAST(context_info AS CHAR(128)), 1, (CHARINDEX(',', CAST(context_info AS CHAR(128)))-1))
                    END
    FROM sys.sysprocesses
    WHERE spid = @@spid

    -- Determine Transaction Type
    IF EXISTS (SELECT * FROM DELETED)
    BEGIN
        SET @XTYPE = 'D'
    END

    IF EXISTS (SELECT * FROM INSERTED)
    BEGIN
        IF (@XTYPE = 'D')
        BEGIN
            SET @XTYPE = 'U'
        END
        ELSE
        BEGIN
            SET @XTYPE = 'I'
        END
    END

    -- Transaction is a Delete
    IF (@XTYPE = 'D')
    BEGIN
        INSERT INTO PS_AUDIT_PSROLEUSR (AUDIT_OPRID, AUDIT_STAMP, AUDIT_ACTN, ROLEUSER, ROLENAME, DYNAMIC_SW)
            SELECT 
                @OPRID, 
                DATEADD(SECOND, ROW_NUMBER() OVER (ORDER BY @OPRID), GETDATE()), 
                'D', ROLEUSER, ROLENAME, DYNAMIC_SW 
            FROM 
                deleted 
    END

    -- Transaction is a Insert
    IF (@XTYPE = 'I')
    BEGIN
        INSERT INTO PS_AUDIT_PSROLEUSR (AUDIT_OPRID, AUDIT_STAMP, AUDIT_ACTN, ROLEUSER, ROLENAME, DYNAMIC_SW)
             SELECT 
                 @OPRID, GETDATE(),
                 'A', ROLEUSER, ROLENAME, DYNAMIC_SW 
             FROM 
                 inserted 
    END

    -- Transaction is a Update
    IF (@XTYPE = 'U')
    BEGIN
        -- Before Update
        INSERT INTO PS_AUDIT_PSROLEUSR (AUDIT_OPRID, AUDIT_STAMP, AUDIT_ACTN, ROLEUSER, ROLENAME, DYNAMIC_SW)
            SELECT 
                @OPRID, GETDATE(), 'K',
                ROLEUSER, ROLENAME, DYNAMIC_SW 
            FROM 
                deleted 
        -- After Update
        INSERT INTO PS_AUDIT_PSROLEUSR (AUDIT_OPRID, AUDIT_STAMP, AUDIT_ACTN, ROLEUSER, ROLENAME, DYNAMIC_SW)
            SELECT 
                @OPRID, GETDATE(), 'N',
                ROLEUSER, ROLENAME, DYNAMIC_SW 
            FROM 
                inserted 
    END

ПРИМЕР СТРОКИ, ВСТАВЛЕННОЙ В PS_AUDIT_PSROLEUSR

AUDIT_OPRID AUDIT_STAMP              AUDIT_ACTN ROLEUSER    ROLENAME    DYNAMIC_SW
K           2019-04-25 08:33:08.340  D          LTESTUSER   EUSER       N
K           2019-04-25 08:33:09.340  D          LTESTUSER   EPRO        N

Не получается получить доступ к таблице «УДАЛИТЬ»в SELECT * FROM DELETED, поэтому я не уверен, что это просто динамически генерируется только во время выполнения.

Примечание: что странно, я запрашиваю PS_AUDIT_PSROLEUSR следующим образом для значения 'K' Iвернуть 0 строкЯ проверил, что позади буквы нет пробелов.

    SELECT *
    FROM PS_AUDIT_PSROLEUSR
    WHERE AUDIT_OPRID = 'K'

Я получаю данные для 'K', только если использую оператор LIKE

-- Results in 0 rows

РЕДАКТИРОВАТЬ:Если я запускаю следующий код, я получаю длину 12 символов для моей строки, вставленной с «K», поэтому что-то добавляет дополнительные завершающие пробелы ...

SELECT LEN(AUDIT_OPRID),*
FROM PS_AUDIT_PSROLEUSR
WHERE AUDIT_OPRID  like 'K%'  

Я также попытался добавить этот простой оператор IF втриггер, однако он тоже, похоже, не работает: я думал, что преобразование двоичного файла (context_info) в varchar позволит логике отфильтровать значение «K».

IF (CONVERT(VARCHAR(256),@OPRID)) <> 'K' 
BEGIN

1 Ответ

2 голосов
/ 30 апреля 2019

Это не обязательно проблема с триггером, а кодировка символов двоичного значения CONTEXT_INFO, установленного пакетным приложением. Вы получите эти симптомы, когда значение CONTEXT_INFO на самом деле является строкой Unicode, а двоичное значение впоследствии преобразуется в T-SQL как CHAR.

Рассмотрим этот пример:

DECLARE @ContextInfo varbinary(128) = CAST(N'ABC,DEF,GHI' AS varbinary(128));
SET CONTEXT_INFO @ContextInfo;
GO

DECLARE @OPRID CHAR(30) = NULL;
SELECT CAST(context_info AS CHAR(30)) AS CharValue, CAST(context_info AS NCHAR(30)) AS NCharValue 
FROM sys.sysprocesses
WHERE spid = @@spid;
GO

Результаты:

+-----------+-------------+
| CharValue | NCharValue  |
+-----------+-------------+
| A         | ABC,DEF,GHI |
+-----------+-------------+

Чтобы решить эту проблему, вы можете либо изменить код приложения, установив CONTEXT_INFO на строку ANSI, либо изменить выражение T-SQL CASE на CAST, указав значение NCHAR. Ниже приведен пример T-SQL, который делает это, а также использует sys.dm_exec_sessions DMV вместо sys.processes, который устарел в течение 15 лет.

SELECT @OPRID = CASE(CHARINDEX(',', CAST(context_info AS NCHAR(64))))
                   WHEN 0 THEN N'Native SQL'
                   ELSE SUBSTRING(CAST(context_info AS NCHAR(64)), 1, (CHARINDEX(',', CAST(context_info AS NCHAR(64)))-1))
                END
FROM sys.dm_exec_sessions
WHERE session_id = @@spid;
...