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


149

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

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

源数据列与数据类型匹配,并且在目标表列的长度定义之内,因此我对可能导致此错误的原因一无所知。


您介意发布一些代码以及有关每个表的信息吗?
凯文·曼塞尔

这些表都很大-因此,我将只发布涉及表的部分定义和代码-这样可以接受吗?
Jim Evans

表的定义和代码会很棒。
IAmTimCorey 2011年

1
上次遇到此问题时,是与触发器有关的,该触发器是在审计表中插入数据。值得检查触发器。
Sachin Vishwakarma

Answers:


185

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


1
根据我上面的评论-即将到来:)
Jim Evans

3
我遇到了同样的问题,不得不比较两个表的所有列类型和大小来解决此问题。
Aziz Shaikh

1
结束工作后,我收集了部分表定义,然后获取了我的存储过程代码,这令人讨厌的列像闪电一样跳了起来……谢谢大家的投入。
Jim Evans

我不能告诉你我做了多少次相同的事情。很高兴您能够解决您的问题。
IAmTimCorey 2011年

我将您的第一个答复标记为答案,因为这是促使我找到答案的原因:)
Jim Evans

86

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

一个简单的解决方案是简单地关闭警告并允许截断发生。因此,如果您收到此错误,但可以确定将旧数据库/表中的数据截断(按比例缩放)是可以接受的,则可以执行以下操作:

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

如上所述,请始终记住之后再重新打开警告。我希望这有帮助。


1
您节省了我几个小时的工作!谢谢我!
Urasquirrel

同样在这里。有时我必须将数据从Web服务存储到表中,其中数据类型仅定义为“字符串”。我不能将 所有内容都设为Varchar(MAX)...
Curt

61

问题很简单:源查询中的一个或多个列包含的数据超出了其目标列的长度。一个简单的解决方案是采用源查询并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-Clause中的谓词过滤。要解决此问题,我使用了LEFT()而不是CAST-只需键入更少的字符。
MikeTeeVee

1
谢谢托马斯,这很奇怪,即使我没有太长的数据,我仍然必须尽快将其转换为新的目标列大小。
米歇尔

15

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

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

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

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

SELECT * FROM sys.messages WHERE message_id = 2628

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'.

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

db <> fiddle演示


更改数据库范围的配置

VERBOSE_TRUNCATION_WARNINGS = {开| 关闭}

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

允许您启用或禁用新的String或二进制数据将被截断的错误消息。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,并且此数据库范围的配置无效。


1
这是现在也可用于SQL Azure的:azure.microsoft.com/en-gb/updates/...
伊恩·坎普

7

造成这种情况的另一个可能原因是,如果您为列设置的默认值超出了列的长度。似乎有人用胖手指表示长度为5但默认值超过长度5的列。这让我发疯了,因为我试图理解为什么即使在我插入的所有内容上它都无法插入任何插入内容整数为1的单列。由于表架构上的默认值具有违背的默认值,因此将其弄乱了-我想这使我们汲取了教训-避免在架构中使用具有默认值的表。:)


1
我认为避免使用默认值不是一个好的解决方案。默认值非常有用。我不会通过删除默认值来解决由错字引起的数据库“问题” ...
Jacob H

3

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


这正是我发生的事情。源/目标表匹配得很好,但是存储的proc定义了一个#table,它的长度较短,并且在那里失败。谢谢!
Joy Walker

3

这可能是一个具有挑战性的错误。这是从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/… ,并且已被列为“审核中”,因此仍有希望。
突击队-恢复莫妮卡

2

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

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 ...然后围绕问题sql运行5-10 sql插入设法查明问题之一,看到字符串太长,增加该列的大小,然后大导入文件没有问题。

有点麻烦和解决方法,但是当您离开时几乎没有选择时,您将尽一切可能。



2

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


2

是的-“将一品脱放入半品脱的锅中是不会的”。我对人们建议的各种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

这样一来,您就可以上下滚动,比较字段长度。带注释的部分让您看到(很明显,一旦被注释掉)是否存在数据类型不匹配,或专门显示字段长度不同的那些-cos我懒得滚动-请注意,整个事情是基于源的与目标名称相匹配的列名称。


我本来要写这样的东西,但是您使它变得简单。非常方便,像魅力一样工作。我能够使用它来比较具有90多个列的表,其中两个立即跳出。谢谢!
Joy Walker

2

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


1

我在创建表时使用了空字符串”,然后在后续更新中收到错误消息“ Msg 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个字符)(如果适用,可以替换),而不会引起截断错误。同样,我只会在与我的警告中所述类似的情况下使用此方法。


1

我建立了一个存储过程,用于分析源表或查询,每列具有几个特征,其中最小长度(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

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

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_前缀为您的存储过程。Microsoft 保留了该前缀以供自己使用(请参阅“ 命名存储过程”,并且您将来确实有可能发生名称冲突。这也不利于存储过程的性能。最好只是简单地避免sp_使用其他东西作为前缀,或者根本不使用前缀!
marc_s 16-10-1

1

当使用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, '')


        --/programming/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


1

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

DBCC TRACEON(460, 1);
GO

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

DBCC TRACEOFF(460, 1);
GO

资源


0

如果您没有足够的权限,也会发生这种情况


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

0

我有一个类似的问题。我正在将数据从一个表复制到除名称以外的所有表中。

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

SELECT *
INTO TEMP_TABLE
FROM SOURCE_TABLE;

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

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

对于Nvarchar(Max),我在TSQL中只能得到4000个字符吗?

希望这可以帮助。


0

当表的列放置约束[主要是长度]时,抛出此错误。。例如,如果列myColumn的数据库架构为CHAR(2),则当您从任何应用程序中调用插入值时,必须传递长度为2的String。

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


0

请尝试以下代码:

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]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.