帮助 SQL Server 触发器在插入前截断错误数据

Posted

技术标签:

【中文标题】帮助 SQL Server 触发器在插入前截断错误数据【英文标题】:Help with SQL Server Trigger to truncate bad data before insert 【发布时间】:2011-05-23 19:56:29 【问题描述】:

我们使用了一项 Web 服务,该服务决定将字段的最大长度从 255 更改。我们有一个旧的供应商表,其上限仍为 255。我们希望使用触发器暂时解决此问题,直到我们可以在下一次迭代中实施对业务更友好的解决方案。

这是我开始的:

CREATE TRIGGER [mySchema].[TruncDescription] 
ON  [mySchema].[myTable] 
INSTEAD OF INSERT
AS 
BEGIN
SET NOCOUNT ON;

INSERT INTO [mySchema].[myTable]
SELECT SubType, type, substring(description, 1, 255)
FROM inserted
END

但是,当我尝试在 myTable 上插入时,出现错误:

字符串或二进制数据将是 截断。该声明已 终止。

我尝试使用SET ANSI_WARNINGS OFF 进行试验,它允许查询工作,但只是没有在描述列中插入任何数据。

有什么方法可以使用触发器来截断过长的数据,或者在设计出更有说服力的解决方案之前,我可以使用另一种替代方法吗?我们在表修改方面相当有限(即我们不能),因为它是一个供应商表,而且我们不控制我们正在使用的 Web 服务,所以我们也不能要求他们修复它。任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

该错误无法避免,因为该错误是在填充插入的表时发生的。

来自文档: http://msdn.microsoft.com/en-us/library/ms191300.aspx

"插入表和删除表的格式与定义INSTEAD OF触发器的表的格式相同。插入表和删除表中的每一列都直接映射到基表中的一列。"

我能想到的唯一真正“聪明”的想法是利用模式和登录使用的默认模式。如果您可以获取 Web 服务用于引用另一个表的登录名,则可以增加该表上的列大小并使用 INSTEAD OF INSERT 触发器对供应商表执行 INSERT。一种变体是在不同的数据库中创建表,并为 Web 服务登录设置默认数据库。

CREATE TRIGGER [myDB].[mySchema].[TruncDescription] 
ON  [myDB].[mySchema].[myTable] 
INSTEAD OF INSERT
AS 
BEGIN
SET NOCOUNT ON;

INSERT INTO [VendorDB].[VendorSchema].[VendorTable]
SELECT SubType, type, substring(description, 1, 255)
FROM inserted
END

【讨论】:

【参考方案2】:

有了这个设置,对我来说一切正常。 不是说显而易见的,但是您确定在测试时描述字段中有数据吗?他们可能会更改您正在插入的其他字段之一,并且其中一个字段可能会引发错误?

CREATE TABLE [dbo].[DataPlay](
    [Data] [nvarchar](255) NULL
) ON [PRIMARY]
GO

还有这样的触发器

Create TRIGGER updT ON  DataPlay 
Instead of Insert 
AS 
BEGIN
    SET NOCOUNT ON;     
INSERT INTO [tempdb].[dbo].[DataPlay]
           ([Data])
           (Select substring(Data, 1, 255) from inserted)
END
GO

然后用

插入
Declare @d as nvarchar(max)
Select @d = REPLICATE('a', 500)
SET ANSI_WARNINGS OFF
INSERT INTO [tempdb].[dbo].[DataPlay]
           ([Data])
     VALUES
           (@d)
GO

【讨论】:

【参考方案3】:

我无法使用以下方法在 SQL 2008 R2 上重现此问题:

Declare @table table ( fielda varchar(10) )

Insert  Into @table ( fielda )
Values  ( Substring('12345678901234567890', 1, 10) )

请确保您的字段确实定义为 varchar(255)。

我还强烈建议您使用带有显式字段列表的 Insert 语句。虽然您的 Insert 在语法上是正确的,但您确实应该使用显式字段列表(就像在我的示例中一样)。问题是当您不指定字段列表时,您将受制于 SQL 和字段顺序的表定义。当您确实使用字段列表时,您可以更改表中字段的顺序(或在中间添加新字段),而无需关心您的插入语句。

【讨论】:

不可否认,我只包含了最基本的代码(以及更改架构、表等)。实际插入符合您的建议。我验证了其他领域,除了这个之外,我们在所有领域都做得很好。 @Darryl 能够给出发生此错误的原因。

以上是关于帮助 SQL Server 触发器在插入前截断错误数据的主要内容,如果未能解决你的问题,请参考以下文章

插入前的 SQL Server 触发器

SQL Server 触发器在插入表之前验证数据

用户将在 SQL Server 中截断字符串或​​二进制数据

SQL Server 字符串或二进制数据将被截断

由于截断/删除导致 SQL Server 主键冲突?

SQL Server字符串或二进制数据将被截断