我参与了一个数据迁移项目。当我尝试将一个表中的数据插入到另一个表中(SQL Server 2005)时,出现以下错误:
Msg 8152,Level 16,State 13,Line 1
字符串或二进制数据被截断。
源数据列与目标表列的数据类型相匹配,并且在长度定义范围内,因此我不知道是什么原因导致了这个错误。
我参与了一个数据迁移项目。当我尝试将一个表中的数据插入到另一个表中(SQL Server 2005)时,出现以下错误:
Msg 8152,Level 16,State 13,Line 1
字符串或二进制数据被截断。
源数据列与目标表列的数据类型相匹配,并且在长度定义范围内,因此我不知道是什么原因导致了这个错误。
您需要发布源表和目标表的表定义,以便我们确定问题出在哪里,但问题的关键是源表中的某列比目标列更大。可能是您以一种您不知道的方式更改了格式。您正在移动的数据库模型也很重要。
正如其他人已经说过的那样,在源表中,你的某个列数据类型比目标列要大。
一个简单的解决方案是关闭警告并允许截断发生。因此,如果你收到了这个错误,但是你确定在你的旧数据库/表中截断数据(将其裁剪至适当大小)是可以接受的,那么你只需执行以下操作:
SET ANSI_WARNINGS OFF;
-- Your insert TSQL here.
SET ANSI_WARNINGS ON;
同上,记得在完成后重新打开警告。
Max(Len( source col ))
,即获取源列的最大长度。Select Max(Len(TextCol1))
, Max(Len(TextCol2))
, Max(Len(TextCol3))
, ...
From ...
然后将这些长度与目标表中的数据类型长度进行比较。至少有一个超过了其目标列长度。
如果您绝对确定这不应该是这种情况 并且不在意是否是这种情况,那么另一种解决方案是将源查询列强制转换为它们的目标长度(这将截断任何过长的数据):
Select Cast(TextCol1 As varchar(...))
, Cast(TextCol2 As varchar(...))
, Cast(TextCol3 As varchar(...))
, ...
From ...
SQL Server 2019将最终返回更有意义的错误消息。
如果您在生产环境中遇到此错误,很难确定该错误来自哪个列或行,以及如何精确定位它。
要启用新行为,您需要使用DBCC TRACEON(460)
。从sys.messages
获取新的错误文本:
SELECT * FROM sys.messages WHERE message_id = 2628
2628 - 表 '%.*ls' 的列 '%.*ls' 中的字符串或二进制数据被截断。截断值:'%.*ls'。
通过启用跟踪标志460,可以在会话或服务器级别上替换消息ID 8152为2628,这条新消息也已经被带回到了SQL Server 2017 CU12(以及即将发布的SQL Server 2016 SP2 CU)。需要注意的是,即使在SQL Server 2019 CTP 2.0中,仍需启用相同的跟踪标志460。在未来的SQL Server 2019版本中,将默认使用消息2628替换消息8152。
SQL Server 2017 CU12也支持此功能。
改进:在SQL Server 2017中,可选替换“字符串或二进制数据将被截断”消息,并提供扩展信息
This SQL Server 2017 update introduces an optional message that contains the following additional context information.
Msg 2628, Level 16, State 6, Procedure ProcedureName, Line Linenumber String or binary data would be truncated in table '%.*ls', column '%.*ls'. Truncated value: '%.*ls'.
The new message ID is 2628. This message replaces message 8152 in any error output if trace flag 460 is enabled.
VERBOSE_TRUNCATION_WARNINGS = { ON | OFF }
适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库
允许您启用或禁用新的字符串或二进制数据会被截断错误消息。SQL Server 2019(15.x)引入了一条新的、更具体的错误消息(2628)来处理这种情况:
在表 '%.*ls' 的列 '%.*ls' 中,字符串或二进制数据将被截断。截断的值: '%.*ls'。
当数据库兼容性级别为150且设置为ON时,截断错误会引发新的错误消息2628,以提供更多上下文信息,并简化故障排除过程。
当数据库兼容性级别为150且设置为OFF时,截断错误会引发先前的错误消息8152。
对于数据库兼容性级别为140或更低的情况,错误消息2628仍然是一条需要启用跟踪标志460的选择性错误消息,此时该数据库作用域配置不起作用。
这可能是另一个原因,如果你为一列设置了默认值并且该默认值超出了该列的长度。看起来有人在一个长度为5的列上输入了超过5的默认值。这让我很烦恼,因为我尝试弄清楚为什么无论我插入什么都不起作用,即使我只插入一个整数1。因为表模式上的默认值违反了该列的限制,它导致了所有问题 - 我认为这给我们带来了教训 - 避免在表模式中使用默认值。 :)
这里有一个略微不同的答案。你的列名和长度可能全部匹配,但是在SELECT语句中你可能将列按错误的顺序指定了。假设tableX和tableY有相同的列名,但是顺序不同。
我将增加另一种可能导致这个错误的原因,只是因为没有人提到过它,它可能会帮助一些未来的人(因为OP已经找到了答案)。如果您要插入的表具有触发器,则可能是触发器生成了错误。当表字段定义被更改但审计表未更改时,我曾经见过这种情况。
DBCC TRACEON(460, 1);
GO
确保在完成操作后将其关闭:
DBCC TRACEOFF(460, 1);
GO
对于其他人,也要检查你们的存储过程。在我的情况下,我的存储过程CustomSearch
中,我无意中声明了一个长度不足的列,因此当我输入大量数据时,尽管我的数据库具有很大的长度,我仍然收到了这个错误。我只需更改自定义搜索中列的长度,错误就消失了。这只是提醒。谢谢。
是的 - "一品脱装不满半品脱壶"。我对各种被建议的存储过程没有什么运气(无论原因是什么),但只要两个表位于同一个数据库中(或者你可以将它们放到同一个数据库中),你就可以使用INFORMATION_SCHEMA.COLUMNS来定位错误字段,方法如下:
select c1.table_name,c1.COLUMN_NAME,c1.DATA_TYPE,c1.CHARACTER_MAXIMUM_LENGTH,c2.table_name,c2.COLUMN_NAME, c2.DATA_TYPE,c2.CHARACTER_MAXIMUM_LENGTH
from [INFORMATION_SCHEMA].[COLUMNS] c1
left join [INFORMATION_SCHEMA].[COLUMNS] c2 on
c1.COLUMN_NAME=c2.COLUMN_NAME
where c1.TABLE_NAME='MyTable1'
and c2.TABLE_NAME='MyTable2'
--and c1.DATA_TYPE<>c2.DATA_TYPE
--and c1.CHARACTER_MAXIMUM_LENGTH <> c2.CHARACTER_MAXIMUM_LENGTH
order by c1.COLUMN_NAME
这将允许您上下滚动,比较字段长度。注释部分可以让您看到(一旦取消注释),是否存在数据类型不匹配,或者特别显示在字段长度方面不同的那些 - 因为我太懒了,不想滚动 - 只要知道整个过程基于源列名与目标列名相匹配。