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

Jim*_*ans 132 t-sql sql-server migration data-migration sql-server-2005

我参与了一个数据迁移项目.当我尝试将数据从一个表插入另一个表(SQL Server 2005)时,我收到以下错误:

消息8152,级别16,状态13,行1
字符串或二进制数据将被截断.

源数据列与数据类型匹配,并且在目标表列的长度定义内,因此我不知道可能导致此错误的原因.

IAm*_*rey 171

您需要发布源表和目标表的表定义,以便我们找出问题所在,但最重要的是源表中的一个列大于目标列.可能是您正在以您不知道的方式更改格式.您正在迁移的数据库模型对于确定这一点非常重要.

  • SQL 不会费心告诉您哪一列导致了问题,这真是太好了。我已经开始从所有错误消息中删除有用的信息,试图模仿这一天才之举。 (13认同)
  • 我遇到了同样的问题,不得不比较两个表的所有列类型和大小来解决问题. (3认同)
  • 在完成收集部分表定义并获取我的存储过程代码之后,有问题的列像闪电一样向我跳来……感谢大家的输入。 (2认同)
  • 对我来说,这主要是因为 MS SQL 在 create 语句中创建了一个没有给定长度且长度为 1 的“varchar”字段。这毫无用处。所以我会检查表 DDL 中是否有“varchars(1)”...或者您的数字是 bif fot int/bigint... (2认同)
  • @AR 这已在 SQL 2017 中修复。 (2认同)

Rud*_*haw 67

正如其他人已经说过的那样,源表中的一个列数据类型大于目标列.

因为这里没有人提到它,一个简单的解决方案(类似于托马斯的__CODE__解决方案)就是简单地关闭警告并允许截断.因此,如果您收到此错误但是您确定可以接受旧数据库/表中的数据被截断(按尺寸切割),则可以执行以下操作:

SET ANSI_WARNINGS  OFF;
-- Your insert TSQL here.
SET ANSI_WARNINGS ON;
Run Code Online (Sandbox Code Playgroud)

如上所述,请务必记住之后再次打开警告.我希望这有帮助.

  • 这里也一样。有时我必须将数据存储到来自 Web 服务的表中,其中数据类型仅定义为“字符串”。我不能把_所有东西_都变成 Varchar(MAX)... (3认同)
  • 绝对不是大规模数据迁移的最佳行动方案,但这足以让我“调试”我的查询,注意到我的其中一列确实正在裁剪输入的字符串。在我的具体场景中,这是迄今为止最快的行动方案。 (2认同)

Tho*_*mas 59

问题非常简单:源查询中的一个或多个列包含的数据超出了其目标列的长度.一个简单的解决方案是获取源查询并Max(Len( source col ))在每列上执行.也就是说,

Select Max(Len(TextCol1))
    , Max(Len(TextCol2))
    , Max(Len(TextCol3))
    , ...
From ...
Run Code Online (Sandbox Code Playgroud)

然后将这些长度与目标表中的数据类型长度进行比较.至少一个超过其目标列长度.

如果你绝对肯定这不应该是这种情况并且不关心它是不是这种情况,那么另一个解决方案是强制将源查询列强制转换为它们的目标长度(这会截断任何太长的数据):

Select Cast(TextCol1 As varchar(...))
    , Cast(TextCol2 As varchar(...))
    , Cast(TextCol3 As varchar(...))
    , ...
From ...
Run Code Online (Sandbox Code Playgroud)

  • 我的日常流程开始因这个错误而中断。我插入的数据总是足够短以适合,并且我总是有其他行(在我从中提取的表中)带有过大的字符串,由于我的过滤器而从未插入这些字符串。也许重建了索引,或者更新了统计数据,但是有一天,机器中的幽灵决定它不再喜欢查询计划,因为它沿着一条数据(太宽)“可能”的路径走下去。在由Where 子句中的谓词过滤之前插入。为了解决这个问题,我使用 LEFT() 而不是 CAST - 只是需要输入更少的字符。 (2认同)
  • 谢谢托马斯,这很奇怪,即使我没有任何太长的数据,我仍然必须将其转换为新的目标列大小,一旦我这样做它就起作用了。 (2认同)

Luk*_*zda 9

SQL Server 2019最终将返回更有意义的错误消息。

二进制或字符串数​​据将被截断=>错误消息增强

如果您在生产中遇到该错误,则看不到该错误来自哪一列或哪一行,以及如何精确定位。

要启用新行为,您需要使用DBCC TRACEON(460)。来自的新错误文本sys.messages

SELECT * FROM sys.messages WHERE message_id = 2628
Run Code Online (Sandbox Code Playgroud)

2628 –表'%。* ls'的列'%。* ls'中的字符串或二进制数据将被截断。截断的值:'%。* ls'。

字符串或二进制数据将被截断:替换臭名昭著的错误8152

此新消息还被反向移植到SQL Server 2017 CU12(以及即将发布的SQL Server 2016 SP2 CU),但默认情况下不是。您需要启用跟踪标志460,以在会话或服务器级别用2628替换消息ID 8152。

请注意,目前,即使在SQL Server 2019 CTP 2.0中,也需要启用相同的跟踪标志460。在将来的SQL Server 2019版本中,默认情况下,消息2628将替换消息8152。


SQL Server 2017 CU12也支持此功能。

改进:在SQL Server 2017中带有扩展信息的“字符串或二进制数据将被截断”消息的可选替换

此SQL Server 2017更新引入了包含以下附加上下文信息的可选消息。

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'.
Run Code Online (Sandbox Code Playgroud)

新消息ID为2628。如果启用了跟踪标志460,此消息将替换任何错误输出中的消息8152。

db <> fiddle演示

  • 现在也可用于 SQL Azure:https://azure.microsoft.com/en-gb/updates/global-trace-flags-are-now-available-in-azure-sql-database-management-instance/ (2认同)
  • 感谢您的链接 - 了解这些变化非常有用。 (2认同)

小智 7

另一个可能的原因是,如果您为超出列长度的列设置了默认值.看起来有人胖了一根长度为5的列,但是默认值超过了5的长度.这让我疯狂,因为我试图理解为什么它不能用于任何插入,即使我插入的是一个整数为1的单个列.因为表模式上的默认值违反了默认值,所以它全部搞砸了 - 我想我们学到了这一点 - 避免在模式中使用默认值的表.:)

  • 我认为避免默认值不是一个好的解决方案。默认值非常有用。我不会通过删除默认值来解决由拼写错误引起的数据库“问题”... (5认同)

HLG*_*GEM 6

我将添加导致此错误的另一个可能原因,只是因为没有人提到它,并且它可能会帮助未来的某个人(因为OP已经找到了他的答案)。如果您要插入的表有触发器,则可能是触发器正在生成错误。我见过这种情况发生在表字段定义更改时,但审计表没有更改时。


Sol*_*lot 6

这是一个略有不同的答案。您的列名和长度可能都匹配,但也许您在 SELECT 语句中以错误的顺序指定了列。假设 tableX 和 tableY 具有名称相同但顺序不同的列


小智 6

如果您\xe2\x80\x99 在 SQL Server 2016-2017 上:\n要修复它,请打开跟踪标志 460

\n\n
DBCC TRACEON(460, 1);\nGO\n
Run Code Online (Sandbox Code Playgroud)\n\n

并确保在以下情况后将其关闭:

\n\n
DBCC TRACEOFF(460, 1);\nGO\n
Run Code Online (Sandbox Code Playgroud)\n\n

来源

\n