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

Posted

技术标签:

【中文标题】SQL Server 字符串或二进制数据将被截断【英文标题】:SQL Server String or binary data would be truncated 【发布时间】:2011-09-17 08:11:03 【问题描述】:

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

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

源数据列与数据类型匹配,并且在目标表列的长度定义范围内,所以我不知道是什么导致了这个错误。

【问题讨论】:

您介意发布一些代码和有关每个表的信息吗? 这两个表都很大——所以我将只发布所涉及的表定义的一部分和代码——这可以接受吗? 表定义和代码会很棒。 上次我遇到这个问题时,是与触发器有关的,触发器是在审计表中插入数据。也值得检查触发器。 【参考方案1】:

您需要发布源表和目标表的表定义,以便我们找出问题所在,但底线是源表中的一列大于目标列。可能是您正在以您不知道的方式更改格式。您要从中迁移的数据库模型对于弄清楚这一点也很重要。

【讨论】:

我遇到了同样的问题,必须比较两个表的所有列类型和大小来解决问题。 在完成收集部分表定义然后获取我的 sproc 代码之后,有问题的列像闪电一样向我跳了出来......谢谢大家的意见。 SQL 懒得告诉你是哪一列导致了问题真是太好了。我已经开始从我的所有错误消息中删除有用的信息,以模仿这种天才的表现。 对我来说,这主要是因为 MS SQL 在长度为 1 的创建语句中创建了一个没有给定长度的“varchar”文件。这只是没用的。所以我会检查我在 DDL 表中是否有“varchars(1)”...或者你的数字是 bif fot int/bigint... @A.R.这已在 SQL 2017 中修复。【参考方案2】:

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

一个简单的解决方案是简单地关闭警告并允许进行截断。因此,如果您收到此错误,但您确定旧数据库/表中的数据可以被截断(按大小),您可以简单地执行以下操作;

SET ANSI_WARNINGS OFF;
-- Your insert TSQL here.
SET ANSI_WARNINGS ON;

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

【讨论】:

这里也一样。有时我必须将数据存储到一个表中,比如一个 Web 服务,其中数据类型仅定义为“字符串”。我不能把 everything 变成 Varchar(MAX)... 这没有工作,但我得到的结果让我能够解决问题(增加 varchar 长度)!所以谢谢。 绝对不是大规模数据迁移的最佳做法,但这足以让我“调试”我的查询,注意到我的一个列确实在裁剪输入的字符串.在我的具体情况下,这是迄今为止最快的行动方案。【参考方案3】:

问题很简单:源查询中的一个或多个列包含超过其目标列长度的数据。一个简单的解决方案是获取您的源查询并在每一列上执行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 ...

【讨论】:

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

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

Binary or string data would be truncated => error message enhancments

如果您有该错误(在生产中),则很难看出该错误来自哪一列或哪一行,以及如何准确定位它。

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

SELECT * FROM sys.messages WHERE message_id = 2628

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

String or Binary data would be truncated: replacing the infamous error 8152

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

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


SQL Server 2017 CU12 也支持此功能。

Improvement: Optional replacement for "String or binary data would be truncated" message with extended information in 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'.

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

db<>fiddle demo


ALTER DATABASE SCOPED CONFIGURATION

VERBOSE_TRUNCATION_WARNINGS = 开 |关闭

适用于:SQL Server(从 SQL Server 2019 (15.x) 开始)和 Azure SQL 数据库

允许您启用或禁用新的字符串或二进制数据 截断的错误消息。 SQL Server 2019 (15.x) 引入了一个新的、更 此方案的特定错误消息 (2628):

String or binary data would be truncated in table '%.*ls', column'%.*ls'. Truncated value: '%.*ls'.

在数据库兼容级别 150 下设置为 ON 时,截断 错误引发新的错误消息 2628 以提供更多上下文和 简化故障排除过程。

在数据库兼容级别 150 下设置为 OFF 时,截断 错误引发先前的错误消息 8152。

对于数据库兼容级别 140 或更低,错误消息 2628 仍然是选择加入错误消息,需要跟踪标志 460 启用,并且此数据库范围配置无效。

【讨论】:

这现在也可用于 SQL Azure:azure.microsoft.com/en-gb/updates/… 感谢您的链接 - 了解这些更改非常有用。【参考方案5】:

另一个可能的原因是,如果您为超过列长度的列设置了默认值。似乎有人胖手指了一个长度为 5 但默认值超过了 5 的列。这让我发疯了,因为我试图理解为什么它在任何插入上都不起作用,即使我插入的只是一个整数为 1 的单列。因为表架构上的默认值违反了默认值,所以它把它搞砸了——我想这让我们学到了教训——避免在架构中使用默认值的表。 :)

【讨论】:

我不认为避免使用默认值是一个好的解决方案。默认值非常有用。我不会通过删除默认值来解决由拼写错误引起的数据库“问题”...【参考方案6】:

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

【讨论】:

!!如此出乎意料,但它奏效了。谢谢! (对我来说,失败的是 INSERT INTO x SELECT * FROM y。)【参考方案7】:

如果您使用的是 SQL Server 2016-2017: 要修复它,请打开跟踪标志 460

DBCC TRACEON(460, 1);
GO

并确保在以下操作后将其关闭:

DBCC TRACEOFF(460, 1);
GO

source

【讨论】:

【参考方案8】:

对于其他人,还请检查您的存储过程。在我的存储过程CustomSearch 中,我不小心声明了我的列的长度不足,所以当我输入大数据时,即使我的数据库有很大的长度,我也会收到该错误。我只是在自定义搜索中更改了列的长度,错误就消失了。这只是为了提醒。谢谢。

【讨论】:

这正是发生在我身上的事。源/目标表匹配得很好,但存储的 proc 定义了一个长度较短的#table,并且在那里失败了。谢谢!【参考方案9】:

这可能是一个具有挑战性的错误。以下是来自https://connect.microsoft.com/SQLServer/feedback/details/339410/ 的一些笔记,请寻找 AmirCharania 的评论。

我已经调整了 AmirCharania 给出的选择到实际表中的数据的答案,而不是临时表。首先将您的数据集选择到开发表中,然后运行以下命令:

WITH CTE_Dev
AS (
    SELECT C.column_id
        ,ColumnName = C.NAME
        ,C.max_length
        ,C.user_type_id
        ,C.precision
        ,C.scale
        ,DataTypeName = T.NAME
    FROM sys.columns C
    INNER JOIN sys.types T ON T.user_type_id = C.user_type_id
    WHERE OBJECT_ID = OBJECT_ID('YOUR TARGET TABLE NAME HERE, WITH SCHEMA')
    )
    ,CTE_Temp
AS (
    SELECT C.column_id
        ,ColumnName = C.NAME
        ,C.max_length
        ,C.user_type_id
        ,C.precision
        ,C.scale
        ,DataTypeName = T.NAME
    FROM sys.columns C
    INNER JOIN sys.types T ON T.user_type_id = C.user_type_id
    WHERE OBJECT_ID = OBJECT_ID('YOUR TEMP TABLE NAME HERE, WITH SCHEMA')
    )
SELECT *
FROM CTE_Dev D
FULL OUTER JOIN CTE_Temp T ON D.ColumnName = T.ColumnName
WHERE ISNULL(D.max_length, 0) < ISNULL(T.max_length, 999)

【讨论】:

看起来 MS 已关闭 Connect 站点。此问题的新链接是:feedback.azure.com/forums/908035-sql-server/suggestions/… ...仍标记为未计划。我认为您所指的评论在迁移发生时(具有讽刺意味地)被截断了。 有趣的是,这个问题再次以一个稍微不同的标题打开:feedback.azure.com/forums/908035-sql-server/suggestions/…,它被列为“审查中”,所以还有希望。【参考方案10】:

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

【讨论】:

【参考方案11】:

是的 - “一品脱放入半品脱的罐子里是不行的”。对于人们建议的各种 SP,我没有太多运气(无论出于何种原因),但是只要这两个表在同一个数据库中(或者您可以将它们放入同一个数据库中),您就可以使用 INFORMATION_SCHEMA。用于定位错误字段的列,因此:

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

这将使您可以上下滚动,并在进行时比较字段长度。注释部分可让您查看(显然,一旦未注释)是否存在数据类型不匹配,或者专门显示字段长度不同的部分 - 因为我懒得滚动 - 请注意整个事情都是基于源与目标的列名匹配的列名。

【讨论】:

我本来打算写这样的东西,但你让它变得简单了。非常方便,工作起来就像一个魅力。我能够用它来比较一个有 90 多列的表,其中两个立即跳出来。谢谢!【参考方案12】:

我今天遇到了这个问题,在寻找这个最小信息错误消息的答案时,我还找到了这个链接:

https://connect.microsoft.com/SQLServer/feedback/details/339410/please-fix-the-string-or-binary-data-would-be-truncated-message-to-give-the-column-name

因此,微软似乎没有计划在短期内扩展错误消息。

所以我转向了其他方式。

我将错误复制到 excel:

(受影响的 1 行)

(受影响的 1 行)

(1 行受影响) 消息 8152,第 16 层,第 14 状态,第 13 行 字符串或二进制数据将被截断。 声明已终止。

(受影响的 1 行)

计算 excel 中的行数,接近导致问题的记录计数器...调整我的导出代码以打印出接近它的 SQL...然后在周围运行 5 - 10 个 sql 插入问题sql并设法查明问题之一,查看过长的字符串,增加该列的大小,然后大导入文件运行没有问题。

有点技巧和解决方法,但是当您离开时别无选择时,您会尽力而为。

【讨论】:

【参考方案13】:

是的,我也面临这样的问题。

REMARKS VARCHAR(500)
to
REMARKS VARCHAR(1000)

在这里,我已将 REMARKS 归档长度从 500 更改为 1000

【讨论】:

【参考方案14】:

我在创建表时使用了空字符串“”,然后在后续更新时收到错误“消息 8152,字符串或二进制数据将被截断”。这是因为更新值包含 6 个字符并且大于预期的列定义。我使用“SPACE”来解决这个问题只是因为我知道在初始数据创建之后我会进行批量更新,即该列不会长时间保持为空。

这里有这么大的警告:这不是一个特别巧妙的解决方案,但在您将数据集组合在一起的情况下很有用,例如对于您正在创建数据挖掘表的一次性情报请求,应用一些批量处理/解释并在结果之前和之后存储以供以后比较/挖掘。这在我的工作中经常发生。

您最初可以使用 SPACE 关键字进行填充,即

    select 
           Table1.[column1]
          ,Table1.[column2]
          ,SPACE(10) as column_name
    into table_you_are_creating
    from Table1
    where ...

随后将允许对“column_name”进行不超过 10 个字符(如适用,可替换)的更新,而不会导致截断错误。同样,我只会在类似于我的警告中描述的场景中使用它。

【讨论】:

【参考方案15】:

我构建了一个存储过程,用于分析源表或查询,每列具有多个特征,其中最小长度 (min_len) 和最大长度 (max_len)。

CREATE PROCEDURE [dbo].[sp_analysetable] (
  @tableName varchar(8000),
  @deep bit = 0
) AS

/*
sp_analysetable 'company'
sp_analysetable 'select * from company where name is not null'
*/

DECLARE @intErrorCode INT, @errorMSG VARCHAR(500), @tmpQ NVARCHAR(2000), @column_name VARCHAR(50), @isQuery bit
SET @intErrorCode=0

IF OBJECT_ID('tempdb..##tmpTableToAnalyse') IS NOT NULL BEGIN
  DROP TABLE ##tmpTableToAnalyse
END
IF OBJECT_ID('tempdb..##tmpColumns') IS NOT NULL BEGIN
  DROP TABLE ##tmpColumns
END

if CHARINDEX('from', @tableName)>0
  set @isQuery=1

IF @intErrorCode=0 BEGIN
  if @isQuery=1 begin
    --set @tableName = 'USE '+@db+';'+replace(@tableName, 'from', 'into ##tmpTableToAnalyse from')
    --replace only first occurance. Now multiple froms may exists, but first from will be replaced with into .. from
    set @tableName=Stuff(@tableName, CharIndex('from', @tableName), Len('from'), 'into ##tmpTableToAnalyse from')
    exec(@tableName)
    IF OBJECT_ID('tempdb..##tmpTableToAnalyse') IS NULL BEGIN
      set @intErrorCode=1
      SET @errorMSG='Error generating temporary table from query.'
    end
    else begin
      set @tableName='##tmpTableToAnalyse'
    end
  end
end

IF @intErrorCode=0 BEGIN
  SET @tmpQ='USE '+DB_NAME()+';'+CHAR(13)+CHAR(10)+'
  select
    c.column_name as [column],
    cast(sp.value as varchar(1000)) as description,
    tc_fk.constraint_type,
    kcu_pk.table_name as fk_table,
    kcu_pk.column_name as fk_column,
    c.ordinal_position as pos,
    c.column_default as [default],
    c.is_nullable as [null],
    c.data_type,
    c.character_maximum_length as length,
    c.numeric_precision as [precision],
    c.numeric_precision_radix as radix,
    cast(null as bit) as [is_unique],
    cast(null as int) as min_len,
    cast(null as int) as max_len,
    cast(null as int) as nulls,
    cast(null as int) as blanks,
    cast(null as int) as numerics,
    cast(null as int) as distincts,
    cast(null as varchar(500)) as distinct_values,
    cast(null as varchar(50)) as remarks
  into ##tmpColumns'
  if @isQuery=1 begin
    SET @tmpQ=@tmpQ+' from tempdb.information_schema.columns c, (select null as value) sp'
  end
  else begin
    SET @tmpQ=@tmpQ+'
      from information_schema.columns c
      left join sysobjects so    on so.name=c.table_name  and so.xtype=''U''
      left join syscolumns sc    on sc.name=c.column_name and sc.id  =so.id 
      left join sys.extended_properties sp on sp.minor_id = sc.colid AND sp.major_id = sc.id and sp.name=''MS_Description''  
      left join information_schema.key_column_usage kcu_fk    on kcu_fk.table_name = c.table_name     and c.column_name = kcu_fk.column_name
      left join information_schema.table_constraints tc_fk    on kcu_fk.table_name = tc_fk.table_name and kcu_fk.constraint_name = tc_fk.constraint_name
      left join information_schema.referential_constraints rc on rc.constraint_name = kcu_fk.constraint_name
      left join information_schema.table_constraints tc_pk    on rc.unique_constraint_name = tc_pk.constraint_name
      left join information_schema.key_column_usage kcu_pk    on tc_pk.constraint_name = kcu_pk.constraint_name
 '
  end
  SET @tmpQ=@tmpQ+' where c.table_name = '''+@tableName+''''

  exec(@tmpQ)
end

IF @intErrorCode=0 AND @deep = 1 BEGIN
  DECLARE
    @count_rows int,
    @count_distinct int,
    @count_nulls int,
    @count_blanks int,
    @count_numerics int,
    @min_len int,
    @max_len int,
    @distinct_values varchar(500)
  DECLARE curTmp CURSOR LOCAL FAST_FORWARD FOR
    select [column] from ##tmpColumns;
  OPEN curTmp
  FETCH NEXT FROM curTmp INTO @column_name
  WHILE @@FETCH_STATUS = 0 and @intErrorCode=0 BEGIN
    set @tmpQ = 'USE '+DB_NAME()+'; SELECT'+
      '  @count_rows=count(0), '+char(13)+char(10)+
      '  @count_distinct=count(distinct ['+@column_name+']),'+char(13)+char(10)+
      '  @count_nulls=sum(case when ['+@column_name+'] is null then 1 else 0 end),'+char(13)+char(10)+
      '  @count_blanks=sum(case when ltrim(['+@column_name+'])='''' then 1 else 0 end),'+char(13)+char(10)+
      '  @count_numerics=sum(isnumeric(['+@column_name+'])),'+char(13)+char(10)+
      '  @min_len=min(len(['+@column_name+'])),'+char(13)+char(10)+
      '  @max_len=max(len(['+@column_name+']))'+char(13)+char(10)+
      ' from ['+@tableName+']'
    exec sp_executesql @tmpQ,
                       N'@count_rows int OUTPUT,
                         @count_distinct int OUTPUT,
                         @count_nulls int OUTPUT,
                         @count_blanks int OUTPUT,
                         @count_numerics int OUTPUT,
                         @min_len int OUTPUT,
                         @max_len int OUTPUT',
                       @count_rows     OUTPUT,
                       @count_distinct OUTPUT,
                       @count_nulls    OUTPUT,
                       @count_blanks    OUTPUT,
                       @count_numerics OUTPUT,
                       @min_len        OUTPUT,
                       @max_len        OUTPUT

    IF (@count_distinct>10) BEGIN
      SET @distinct_values='Many ('+cast(@count_distinct as varchar)+')'
    END ELSE BEGIN
      set @distinct_values=null
      set @tmpQ = N'USE '+DB_NAME()+';'+
        '  select @distinct_values=COALESCE(@distinct_values+'',''+cast(['+@column_name+'] as varchar),  cast(['+@column_name+'] as varchar))'+char(13)+char(10)+
        '  from ('+char(13)+char(10)+
        '    select distinct ['+@column_name+'] from ['+@tableName+'] where ['+@column_name+'] is not null) a'+char(13)+char(10)
      exec sp_executesql @tmpQ,
                         N'@distinct_values varchar(500) OUTPUT',
                         @distinct_values        OUTPUT
    END
    UPDATE ##tmpColumns SET
      is_unique      =case when @count_rows=@count_distinct then 1 else 0 end,
      distincts      =@count_distinct,
      nulls          =@count_nulls,
      blanks         =@count_blanks,
      numerics       =@count_numerics,
      min_len        =@min_len,
      max_len        =@max_len,
      distinct_values=@distinct_values,
      remarks       =
        case when @count_rows=@count_nulls then 'all null,' else '' end+
        case when @count_rows=@count_distinct then 'unique,' else '' end+
        case when @count_distinct=0 then 'empty,' else '' end+
        case when @min_len=@max_len then 'same length,' else '' end+
        case when @count_rows=@count_numerics then 'all numeric,' else '' end
    WHERE [column]=@column_name

    FETCH NEXT FROM curTmp INTO @column_name
  END
  CLOSE curTmp DEALLOCATE curTmp
END

IF @intErrorCode=0 BEGIN
  select * from ##tmpColumns order by pos
end

IF @intErrorCode=0 BEGIN --Clean up temporary tables
  IF OBJECT_ID('tempdb..##tmpTableToAnalyse') IS NOT NULL BEGIN
    DROP TABLE ##tmpTableToAnalyse
  END
  IF OBJECT_ID('tempdb..##tmpColumns') IS NOT NULL BEGIN
    DROP TABLE ##tmpColumns
  END
end

IF @intErrorCode<>0 BEGIN
  RAISERROR(@errorMSG, 12, 1)
END
RETURN @intErrorCode

我将此过程存储在主数据库中,以便可以在每个数据库中使用它,如下所示:

sp_analysetable 'table_name', 1
// deep=1 for doing value analyses

输出是:

column description constraint_type fk_table fk_column pos default null data_type length precision radix is_unique min_len max_len nulls blanks numerics distincts distinct_values remarks id_individual NULL PRIMARY KEY NULL NULL 1 NULL NO int NULL 10 10 1 1 2 0 0 70 70 Many (70) unique,all numeric, id_brand NULL NULL NULL NULL 2 NULL NO int NULL 10 10 0 1 1 0 0 70 2 2,3 same length,all numeric, guid NULL NULL NULL NULL 3 (newid()) NO uniqueidentifier NULL NULL NULL 1 36 36 0 0 0 70 Many (70) unique,same length, customer_id NULL NULL NULL NULL 4 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, email NULL NULL NULL NULL 5 NULL YES varchar 100 NULL NULL 0 4 36 0 0 0 31 Many (31) mobile NULL NULL NULL NULL 6 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, initials NULL NULL NULL NULL 7 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, title_short NULL NULL NULL NULL 8 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, title_long NULL NULL NULL NULL 9 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, firstname NULL NULL NULL NULL 10 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, lastname NULL NULL NULL NULL 11 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, address NULL NULL NULL NULL 12 NULL YES varchar 100 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, pc NULL NULL NULL NULL 13 NULL YES varchar 10 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, kixcode NULL NULL NULL NULL 14 NULL YES varchar 20 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty, date_created NULL NULL NULL NULL 15 (getdate()) NO datetime NULL NULL NULL 1 19 19 0 0 0 70 Many (70) unique,same length, created_by NULL NULL NULL NULL 16 (user_name()) NO varchar 50 NULL NULL 0 13 13 0 0 0 1 loyalz-public same length, id_location_created NULL FOREIGN KEY location id_location 17 NULL YES int NULL 10 10 0 1 1 0 0 70 2 1,2 same length,all numeric, id_individual_type NULL FOREIGN KEY individual_type id_individual_type 18 NULL YES int NULL 10 10 0 NULL NULL 70 0 0 0 NULL all null,empty, optin NULL NULL NULL NULL 19 NULL YES int NULL 10 10 0 1 1 39 0 31 2 0,1 same length,

【讨论】:

旁注:您应该为您的存储过程使用sp_ 前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免 sp_ 并使用其他东西作为前缀 - 或者根本不使用前缀!【参考方案16】:

我编写了一个有用的存储过程来帮助识别和解决使用 INSERT SELECT 语句时出现的文本截断问题(字符串或二进制数据会被截断)。它仅比较字段 CHAR、VARCHAR、NCHAR 和 NVARCHAR,并在可能导致错误的情况下逐个字段返回评估字段。

EXEC dbo.GetFieldStringTruncate SourceTableName, TargetTableName

这个存储过程是针对INSERT SELECT语句时文本截断的问题。

此存储过程的操作取决于用户先前确定存在问题的 INSERT 语句。然后将源数据插入到全局临时表中。建议使用 SELECT INTO 语句。

您必须在 SELECT 语句的每个字段的别名中使用与目标表的字段相同的名称。

功能代码:

DECLARE @strSQL nvarchar(1000)
IF NOT EXISTS (SELECT * FROM dbo.sysobjects where id = OBJECT_ID(N'[dbo].[GetFieldStringTruncate]'))
    BEGIN
        SET @strSQL = 'CREATE PROCEDURE [dbo].[GetFieldStringTruncate] AS RETURN'
        EXEC sys.sp_executesql @strSQL
    END

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

/*
------------------------------------------------------------------------------------------------------------------------
    Description:    
                    Syntax 
                    ---------------
                    dbo.GetFieldStringTruncate(SourceTable, TargetTable)
                    +---------------------------+-----------------------+
                    |   SourceTableName         |   VARCHAR(255)        |
                    +---------------------------+-----------------------+
                    |   TargetTableName         |   VARCHAR(255)        |
                    +---------------------------+-----------------------+

                    Arguments
                    ---------------
                    SourceTableName
                    The name of the source table. It should be a temporary table using double charp '##'. E.g. '##temp'

                    TargetTableName
                    The name of the target table. It is the table that receives the data used in the INSERT INTO stament.

                    Return Type
                    ----------------
                    Returns a table with a list of all the fields with the type defined as text and performs an evaluation indicating which field would present the problem of string truncation.

                    Remarks
                    ----------------
                    This stored procedure is oriented to the problem of text truncation when an INSERT SELECT statement is made.
                    The operation of this stored procedure depends on the user previously identifying the INSERT statement with the problem. Then inserting the source data into a global temporary table. The SELECT INTO statement is recommended.
                    You must use the same name of the field of the destination table in the alias of each field of the SELECT statement.

                    Examples
                    ====================================================================================================

                    --A. Test basic

                        IF EXISTS (SELECT * FROM sys.objects  WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[tblDestino]') AND TYPE IN (N'U'))
                            DROP TABLE tblDestino

                        CREATE TABLE tblDestino
                        (
                            Id INT IDENTITY,
                            Field1 VARCHAR(10),
                            Field2 VARCHAR(12),
                            Field3 VARCHAR(11),
                            Field4 VARCHAR(16),
                            Field5 VARCHAR(5),
                            Field6 VARCHAR(1),
                            Field7 VARCHAR(1),
                            Field8 VARCHAR(6),
                            Field9 VARCHAR(6),
                            Field10 VARCHAR(50),
                            Field11 VARCHAR(50),
                            Field12 VARCHAR(50)
                        )

                        INSERT INTO dbo.tblDestino
                        (
                             Field1 ,
                             Field2 ,
                             Field3 ,
                             Field4 ,
                             Field5 ,
                             Field6 ,
                             Field7 ,
                             Field8 ,
                             Field9 ,
                             Field10 ,
                             Field11 ,
                             Field12
                            )
                        SELECT 
                             '123456789' , -- Field1 - varchar(10)
                             '123456789' , -- Field2 - varchar(12)
                             '123456789' , -- Field3 - varchar(11)
                             '123456789' , -- Field4 - varchar(16)
                             '123456789' , -- Field5 - varchar(5)
                             '123456789' , -- Field6 - varchar(1)
                             '123456789' , -- Field7 - varchar(1)
                             '123456789' , -- Field8 - varchar(6)
                             '123456789' , -- Field9 - varchar(6)
                             '123456789' , -- Field10 - varchar(50)
                             '123456789' , -- Field11 - varchar(50)
                             '123456789'  -- Field12 - varchar(50)
                        GO  

                    Result:
                        String or binary data would be truncated


                    *Here you get the truncation error. Then, we proceed to save the information in a global temporary table. 
                    *IMPORTANT REMINDER: You must use the same name of the field of the destination table in the alias of each field of the SELECT statement.


                    Process:

                        IF OBJECT_ID('tempdb..##TEMP') IS NOT NULL DROP TABLE ##TEMP
                        go
                        SELECT 
                             [Field1] = '123456789' ,
                             [Field2] = '123456789' ,
                             [Field3] = '123456789' ,
                             [Field4] = '123456789' ,
                             [Field5] = '123456789' ,
                             [Field6] = '123456789' ,
                             [Field7] = '123456789' ,
                             [Field8] = '123456789' ,
                             [Field9] = '123456789' ,
                             [Field10] = '123456789' ,
                             [Field11] = '123456789' ,
                             [Field12] = '123456789'  
                        INTO ##TEMP

                    Result:
                    (1 row(s) affected)

                    Test:
                        EXEC dbo.GetFieldStringTruncate @SourceTableName = '##TEMP', @TargetTableName = 'tblDestino'

                    Result:

                        (12 row(s) affected)
                        ORIGEN Nombre Campo        ORIGEN Maximo Largo  DESTINO Nombre Campo     DESTINO Tipo de campo   Evaluación
                        -------------------------- -------------------- ------------------------ ----------------------- -------------------------
                        Field1                     9                    02 - Field1              VARCHAR(10)             
                        Field2                     9                    03 - Field2              VARCHAR(12)             
                        Field3                     9                    04 - Field3              VARCHAR(11)             
                        Field4                     9                    05 - Field4              VARCHAR(16)             
                        Field5                     9                    06 - Field5              VARCHAR(5)              possible field with error
                        Field6                     9                    07 - Field6              VARCHAR(1)              possible field with error
                        Field7                     9                    08 - Field7              VARCHAR(1)              possible field with error
                        Field8                     9                    09 - Field8              VARCHAR(6)              possible field with error
                        Field9                     9                    10 - Field9              VARCHAR(6)              possible field with error
                        Field10                    9                    11 - Field10             VARCHAR(50)             
                        Field11                    9                    12 - Field11             VARCHAR(50)             
                        Field12                    9                    13 - Field12             VARCHAR(50)             

                    ====================================================================================================

    ------------------------------------------------------------------------------------------------------------

    Responsible:    Javier Pardo 
    Date:           October 19/2018
    WB tests:       Javier Pardo 

    ------------------------------------------------------------------------------------------------------------

*/

ALTER PROCEDURE dbo.GetFieldStringTruncate
(
    @SourceTableName AS VARCHAR(255)
    , @TargetTableName AS VARCHAR(255)
)
AS
BEGIN
    BEGIN TRY

        DECLARE @colsUnpivot AS NVARCHAR(MAX),
            @colsUnpivotConverted AS NVARCHAR(MAX),
           @query  AS NVARCHAR(MAX)

        SELECT @colsUnpivot = stuff((
                    SELECT DISTINCT ',' + QUOTENAME(col.NAME)
                    FROM tempdb.sys.tables tab
                    INNER JOIN tempdb.sys.columns col
                        ON col.object_id = tab.object_id
                    INNER JOIN tempdb.sys.types typ
                        ON col.system_type_id = TYP.system_type_id
                    WHERE tab.NAME = @SourceTableName
                    FOR XML path('')
                    ), 1, 1, '')
                ,@colsUnpivotConverted = stuff((
                    SELECT DISTINCT ',' + 'CONVERT(VARCHAR(MAX),' + QUOTENAME(col.NAME) + ') AS ' + QUOTENAME(col.NAME)
                    FROM tempdb.sys.tables tab
                    INNER JOIN tempdb.sys.columns col
                        ON col.object_id = tab.object_id
                    INNER JOIN tempdb.sys.types typ
                        ON col.system_type_id = TYP.system_type_id
                    WHERE tab.NAME = @SourceTableName
                    FOR XML path('')
                    ), 1, 1, '')


        --https://***.com/questions/11158017/column-conflicts-with-the-type-of-other-columns-in-the-unpivot-list
        IF OBJECT_ID('tempdb..##TablaConMaximos') IS NOT NULL DROP TABLE ##TablaConMaximos

        set @query 
          = 'SELECT u.d AS colname, MAX(LEN(u.data)) as [maximo_largo]
            INTO ##TablaConMaximos
            FROM 
            (
                SELECT ' + @colsUnpivotConverted + '
                FROM ' + @SourceTableName + '
            ) T
            UNPIVOT
             (
                data
                for d in ('+ @colsunpivot +')
             ) u
             GROUP BY u.d'

        PRINT @query

        exec sp_executesql @query;

        ------------------------------------------------------------------------------------------------------------
        SELECT --'Nombre de campo' = RIGHT('00' + ISNULL(CONVERT(VARCHAR,col.column_id),''),2) + ' - ' + col.name + ' '
            --, 'Tipo de campo' = ISNULL(CONVERT(VARCHAR,upper(typ.name)),'') + '(' + ISNULL(CONVERT(VARCHAR,col.max_length),'') + ')'
            [ORIGEN Nombre Campo] = tcm.colname
            , [ORIGEN Maximo Largo] = tcm.maximo_largo
            , [DESTINO Nombre Campo] = DESTINO.[Nombre de campo]
            , [DESTINO Tipo de campo] = DESTINO.[Tipo de campo]
            , [Evaluación] = CASE WHEN DESTINO.maximo_largo < tcm.maximo_largo THEN 'possible field with error' ELSE '' END
            --, * 
        FROM tempdb.sys.tables tab
            INNER JOIN tempdb.sys.columns col
                ON col.object_id = tab.object_id
            INNER JOIN tempdb.sys.types typ
                ON col.system_type_id = TYP.system_type_id
            RIGHT JOIN 
                (
                    SELECT column_id
                        , [Nombre de campo] = RIGHT('00' + ISNULL(CONVERT(VARCHAR,col.column_id),''),2) + ' - ' + col.name + ' '
                        , [Tipo de campo] = ISNULL(CONVERT(VARCHAR,upper(typ.name)),'') + '(' + ISNULL(CONVERT(VARCHAR,col.max_length),'') + ')'
                        , [maximo_largo] = col.max_length
                        , [colname] = col.name
                    FROM sys.tables tab
                        INNER JOIN sys.columns col
                            ON col.object_id = tab.object_id
                        INNER JOIN sys.types typ
                            ON col.system_type_id = TYP.system_type_id
                    WHERE tab.NAME = @TargetTableName
                ) AS DESTINO
                    ON col.name = DESTINO.colname
            INNER JOIN ##TablaConMaximos tcm
                ON tcm.colname = DESTINO.colname

        WHERE tab.NAME = @SourceTableName
            AND typ.name LIKE '%char%'
        ORDER BY col.column_id

    END TRY
    BEGIN CATCH
        SELECT 'Internal error ocurred' AS Message
    END CATCH   

END

目前只支持数据类型CHAR、VARCHAR、NCHAR和NVARCHAR。您可以在下面的下一个链接中找到此代码的最新版本,我们互相帮助改进它。 GetFieldStringTruncate.sql

https://gist.github.com/jotapardo/210e85338f87507742701aa9d41cc51d

【讨论】:

【参考方案17】:

当您没有足够的权限时也会发生这种情况

【讨论】:

真的吗?实际的“字符串或二进制数据将被截断”错误?如果您没有权限,这似乎是一个非常奇怪的错误。是否有阻止您写入超过一定数量数据的权限? (我很感兴趣,因为我想在收到此错误时自动检查字段大小 - 所以如果它可能由于其他原因而发生,那非常有趣!)【参考方案18】:

我遇到了类似的问题。我正在将数据从一个表复制到一个相同的表中,但名称除外。

最终我使用 SELECT INTO 语句将源表转储到临时表中。

SELECT *
INTO TEMP_TABLE
FROM SOURCE_TABLE;

我将源表的架构与临时表进行了比较。当我期待varchar(250) 时,我发现其中一列是varchar(4000)

更新: 如果您有兴趣,可以在此处解释 varchar(4000) 问题:

For Nvarchar(Max) I am only getting 4000 characters in TSQL?

希望这会有所帮助。

【讨论】:

【参考方案19】:

当表的列放置约束 [ 主要是长度 ] 时会引发此错误。 .例如。如果 myColumn 列的数据库架构是 CHAR(2),那么当您从任何应用程序调用插入值时,您必须传递长度为 2 的字符串。

错误基本上说明了;长度为 3 及以上的字符串与数据库模式指定的长度限制不一致。这就是 SQL Server 发出警告并引发数据丢失/截断错误的原因。

【讨论】:

【参考方案20】:

请尝试以下代码:

CREATE TABLE [dbo].[Department](
    [Department_name] char(10) NULL
)

INSERT INTO [dbo].[Department]([Department_name]) VALUES  ('Family Medicine')
--error will occur

 ALTER TABLE [Department] ALTER COLUMN [Department_name] char(50)

INSERT INTO [dbo].[Department]([Department_name]) VALUES  ('Family Medicine')

select * from [Department]

【讨论】:

以上是关于SQL Server 字符串或二进制数据将被截断的主要内容,如果未能解决你的问题,请参考以下文章

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

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

SQL 字符串或二进制数据将被截断,列名

sql server,将截断字符串或二进制数据,如何知道出错在哪个字段?

SQLException : 字符串或二进制数据将被截断

字符串或二进制数据将被截断。该语句已终止。不明白为啥? [复制]