触发仅将变量的 1 个字符插入表中
Posted
技术标签:
【中文标题】触发仅将变量的 1 个字符插入表中【英文标题】:Trigger only inserting 1 character of variable into table 【发布时间】:2019-04-25 17:12:59 【问题描述】:我有以下 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
中的“DELETE”表,所以我不确定这是否只是在运行时动态生成的。
旁注 - 奇怪的是我如下查询 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
【问题讨论】:
尾随空格很可能是由 CHAR 数据类型创建的。例如 CHAR(30) 将用空格填充您的值,以便长度始终为 30 个字符长。如果您不希望它们有多余的空格,则可以使用 VARCHAR(30) 。旁注 - 如果您计划使用 Unicode 字符 NVARCHAR(30) 将被使用。 (前面的“N”表示允许Unicode字符)。 这个奇怪的事情是,当用户自己更新应用程序中的数据时,它目前可以正常工作,它将整个 OPRID 插入列中。当批处理程序更新数据时,它只是表现出这种行为。我不打算输出任何 unicode 字符。 我在上面的代码中看不到任何会导致这种情况的东西。请提供您用于在批处理程序中设置上下文信息的代码。我的猜测是您没有正确设置它,或者它在那里被截断了。 附带说明,是的,inserted
和 deleted
表是动态创建和填充的,并且仅在某些情况下可用,triggers 就是其中之一。
@pcdev 我将不得不做更多的挖掘工作,因为老实说我不确定程序是如何做到这一点的,它必须在比我编码的水平更低的水平上完成。
【参考方案1】:
这不一定是触发器的问题,而是批处理应用程序设置时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 而不是已弃用 15 年的 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;
【讨论】:
以上是关于触发仅将变量的 1 个字符插入表中的主要内容,如果未能解决你的问题,请参考以下文章