2

PS_AUDIT_PSROLEUSR我有以下 sql 触发器,它根据相关表上发生的 Delete 操作将数据插入名为的表中。

当应用程序中的在线用户触发表更新(删除角色行)时,整个 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

我似乎无法访问 'DELETE' 表,SELECT * FROM DELETED所以我不确定这是否只是在运行时动态生成的。

旁注 - 奇怪的是我查询 PS_AUDIT_PSROLEUSR 如下值'K'我得到0行返回。我检查了字母后面没有尾随空格。

    SELECT *
    FROM PS_AUDIT_PSROLEUSR
    WHERE AUDIT_OPRID = 'K'

LIKE如果我使用运算符,我只会获得“K”的数据

-- 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
4

1 回答 1

2

这不一定是触发器的问题,而是CONTEXT_INFO批处理应用程序设置时二进制值的字符编码问题。当CONTEXT_INFO值实际上是一个 Unicode 字符串并且二进制值随后被转换为CHART-SQL 时,您将得到这些症状。

考虑这个例子:

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 表达式更改CASTNCHAR. 下面是执行此操作的 T-SQL 示例,它还使用sys.dm_exec_sessionsDMV 而不是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;
于 2019-04-30T11:41:21.637 回答