【问题标题】:SQL trigger: Change nvarchar sizeSQL 触发器:更改 nvarchar 大小
【发布时间】:2018-02-15 02:07:44
【问题描述】:

我有几个列 nvarchar(max) 的表。我正在构建一个触发器,它试图将单元格存储在 sql_Variant 中。 Nvarchar(max) 不能存储在 sql_variant 中。如何检查它是否是 nvarchar(max) 然后将其转换为 nvarchar(4000) 以便避免这种情况?

编辑:很可能错误发生在这里:

set @sql = 'select @audit_oldvalue=[' +@Item +'] from #tempTrigT';

因为 @audit_oldvalue 是 sql_variant 但 #tempTrigT 中的 [' +@Item +'] 可以是 nvarchar(max)

如果我在输入表中编辑某些内容,我会收到此错误:

操作数类型冲突 nvarchar(max) 与 sql_variant 不兼容

表结构:

输入表

输出表

我要转换的代码:

Select * into #tempTrigT from (select * from deleted where @Action in ( 'U','D')) A UNION (select * from inserted where @Action ='I') 

Select @sql = @sql + 'Case when IsNull(i.[' + Column_Name +  
'],0) = IsNull(d.[' + Column_name + '],0) then ''''  
 else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +' 
from information_schema.columns  
where table_name = 'Forms' and column_name <>'rowguid' and column_name <>'modifieddate'
--Define output parameter 
set @ParmDefinition = '@OutString varchar(max) OUTPUT' 
--Format sql 
set @sql = 'Select @OutString = '  
+ Substring(@sql,1 , len(@sql) -1) +  
' From dbo.Forms i  ' --Will need to be updated for target schema 
+ ' inner join #tempTrigT d on 
i.id = d.id'  --Will need to be updated for target schema 
--Execute sql and retrieve desired column list in output parameter 
exec sp_executesql @sql, @ParmDefinition, @OutString OUT


DECLARE @Items VARCHAR(max)

 set @Items = @OutString;

 DECLARE @Item VARCHAR(50)
 DECLARE @Pos INT
 DECLARE @Loop BIT
 SELECT @Loop = CASE WHEN LEN(@OutString) > 0 THEN 1 ELSE 0 END
 WHILE (SELECT @Loop) = 1
 BEGIN
 SELECT @Pos = CHARINDEX(',', @OutString, 1)
 IF @Pos > 0
 BEGIN
 SELECT @Item = SUBSTRING(@OutString, 1, @Pos - 1)
 SELECT @OutString = SUBSTRING(@OutString, @Pos + 1, LEN(@OutString) - @Pos)
----------------------------------

set @audit_field = @Item;

set @sql = 'select @audit_oldvalue=[' +@Item +'] from #tempTrigT'; --ERROR LINE
EXEC SP_EXECUTESQL @sql,N'@audit_oldvalue sql_variant OUTPUT',@audit_oldvalue OUTPUT -- If inserted @audit_oldvalue gets the new value

set @sql = 'select @audit_value=i.[' +@Item +'] from dbo.Forms i  inner join #tempTrigT d on i.id = d.id';
EXEC SP_EXECUTESQL @sql,N'@audit_value sql_variant OUTPUT',@audit_value OUTPUT


if @Action = 'U'
begin
    insert into [dbo].[AuditTrailForms]([TSid],[TSField],[OldValue],[NewValue],[changedate],[Change_Action],[Change_user],[Columns_Updated])
    select id,@audit_field, @audit_oldvalue, @audit_value,getdate(),@Action, coalesce(ModifiedBy,suser_name()), @Items
    from inserted 

end

     END
 ELSE
 BEGIN
 SELECT @Item = @OutString
 SELECT @Loop = 0
 END
 END

【问题讨论】:

  • 您使用的是哪个 dbms?
  • @jarih sql server 2012
  • 您尝试过“CONVERT(nvarchar(4000), @nvarcharmaxparam)”吗?
  • cast(left(yourColumn),4000) as varchar(4000))
  • @sarslan 请检查编辑后的帖子

标签: tsql stored-procedures sql-server-2012 triggers type-conversion


【解决方案1】:

您应该可以将其转换为 VARCHAR(4000)。

Select @sql = @sql + 'Case when IsNull(CAST(i.[' + Column_Name +  
'] AS VARCHAR(4000)),0) = IsNull(CAST(d.[' + Column_name + '] AS VARCHAR(4000)),0) then ''''  
 else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +' 
from information_schema.columns  
where table_name = 'Forms' and column_name <>'rowguid' and column_name <>'modifieddate'

或者,如果您只想对这些列执行此操作(除非出于性能原因,否则我认为这不会成为问题)。

Select @sql = @sql + 'Case when IsNull(' 
    + CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN 'CAST(' ELSE '' END 
    + 'i.[' + Column_Name +  ']' 
    + CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN ' AS VARCHAR(4000))' ELSE '' END + 
    ',0) = IsNull('
    + CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN 'CAST(' ELSE '' END 
    + 'd.[' + Column_name + ']'
    + CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN ' AS VARCHAR(4000))' ELSE '' END + 
    + ',0) then ''''  else ' 
    + quotename(Column_Name, char(39)) + ' + '',''' + ' end +' 
from information_schema.columns  
where table_name = 'Forms' and column_name <>'rowguid' and column_name <>'modifieddate'

【讨论】:

  • OP 已经多次发布关于这段代码的问题,尽管问题不同,但到目前为止还没有接受任何答案(我见过)。不确定OP是否清楚实际需要什么。 (抱歉,如果地方不对,没有足够的代表,所以只能评论我自己的帖子)
  • 感谢您的回答。我提出了不同的问题,因为在同一段代码中存在不同的主题。我当然接受了问题。
  • 我测试了第二个 sn-p 但我再次收到相同的错误。我不想转换 VARCHAR(4000) 中的所有内容,这就是我选择 sn-p #2 的原因
  • 没什么。但我想现在错误发生在这一行: set sql = 'select @audit_oldvalue=[' +Item +'] from #tempTrigT'; EXEC SP_EXECUTESQL sql,N'@audit_oldvalue sql_variant OUTPUT',@audit_oldvalue OUTPUT 其中@Item 包含发生更改的列。所以它从 tempTrigT 获取 Item 列,它是 nvarchar(max) 并将其存储为 sql_variant
  • 什么是 tempTrigT?该表的创建未包含在您的帖子中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
  • 1970-01-01
  • 2021-06-11
  • 2021-08-04
  • 1970-01-01
  • 2020-03-24
相关资源
最近更新 更多