我有以下sql触发器,该触发器基于相关表上发生的Delete操作将数据插入名为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
中的'DELETE'表,因此我不确定这是否仅在运行时动态生成。
侧面说明-有点奇怪的是,我如下查询PS_AUDIT_PSROLEUSR作为值'K',我得到0行返回。我检查了字母后面是否没有尾随空格。
SELECT *
FROM PS_AUDIT_PSROLEUSR
WHERE AUDIT_OPRID = 'K'
如果我使用LIKE
运算符,我只会获得'K'的数据
-- Results in 0 rows
编辑:如果运行以下代码,则插入'K'的行的长度为12个字符,因此会增加额外的尾随空格...
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
答案 0 :(得分:2)
这不一定是触发器的问题,而是由批处理应用程序设置时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示例,该示例还使用了已弃用15年的sys.dm_exec_sessions
DMV而不是sys.processes
。
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;