Строка SQL Server или двоичные данные будут усечены - PullRequest
119 голосов
/ 17 июня 2011

Я участвую в проекте по миграции данных. Я получаю следующую ошибку при попытке вставить данные из одной таблицы в другую таблицу (SQL Server 2005):

Сообщение 8152, Уровень 16, Состояние 13, Строка 1
Строковые или двоичные данные будут обрезаны.

Исходные столбцы данных соответствуют типу данных и находятся в пределах определений длины столбцов таблицы назначения, поэтому я не понимаю, что может быть причиной этой ошибки.

Ответы [ 19 ]

155 голосов
/ 17 июня 2011

Вам нужно будет опубликовать определения таблиц для исходной и целевой таблиц, чтобы мы могли выяснить, где проблема, но Суть в том, что один из ваших столбцов в исходной таблице больше, чем столбцы назначения .Может случиться так, что вы меняете форматы так, как вы не знали.Модель базы данных, из которой вы переходите, также важна для выяснения этого.

58 голосов
/ 17 июня 2011

Проблема довольно проста: один или несколько столбцов в исходном запросе содержат данные, длина которых превышает длину столбца назначения. Простым решением было бы взять исходный запрос и выполнить 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 ...
57 голосов
/ 22 февраля 2015

Как уже говорили другие, типы данных одного из столбцов в исходной таблице больше, чем столбцы назначения.

Простое решение - просто отключить предупреждение и разрешить усечение. Итак, если вы получаете эту ошибку, но уверены, что данные в вашей старой базе данных / таблице допустимы для усечения (обрезки по размеру), вы можете просто сделать следующее:

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

Как и выше, всегда не забывайте снова включать предупреждения. Надеюсь, это поможет.

6 голосов
/ 17 февраля 2016

Еще одна потенциальная причина этого - наличие настройки по умолчанию для столбца, длина которого превышает длину столбца. Похоже, кто-то толстый перебрал столбец, который имел длину 5, но значение по умолчанию превысило длину 5. Это сводило меня с ума, когда я пытался понять, почему он не работает ни на одной вставке, даже если все, что я вставлял, было один столбец с целым числом 1. Поскольку значение по умолчанию в схеме таблицы имело это нарушающее значение по умолчанию, оно все испортило - что, я думаю, подводит нас к извлеченному уроку - избегайте таблиц со значениями по умолчанию в схеме. :)

3 голосов
/ 03 мая 2016

Это может быть сложной ошибкой.Вот некоторые заметки, взятые из 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)
3 голосов
/ 11 августа 2014

Для остальных также проверьте вашу хранимую процедуру .В моем случае в моей хранимой процедуре CustomSearch я случайно объявил недостаточную длину для своего столбца, поэтому, когда я вводил большие данные, я получал эту ошибку, даже если в моей базе данных была большая длина.Я просто изменил длину моего столбца в моем пользовательском поиске, ошибка исчезла.Это только для напоминания.Спасибо.

2 голосов
/ 25 мая 2016

Да, я тоже сталкиваюсь с такой проблемой.

REMARKS VARCHAR(500)
to
REMARKS VARCHAR(1000)

Здесь я изменил длину поля ЗАМЕЧАНИЯ с 500 на 1000

2 голосов
/ 29 января 2016

Я столкнулся с этой проблемой сегодня, и в процессе поиска ответа на это минимальное информативное сообщение об ошибке я также нашел эту ссылку:

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

Так что, похоже, у Microsoft нетпланирует расширить сообщение об ошибке в ближайшее время.

Так что я обратился к другим средствам.

Я скопировал ошибки в Excel:

(затронут 1 строка)

(затронут 1 строка)

(1 строка(s) затронуты) Сообщение 8152, уровень 16, состояние 14, строка 13 Строка или двоичные данные будут обрезаны.Оператор был прерван.

(затронуто 1 строк)

посчитал количество строк в Excel, приблизился к счетчику записей, вызвавшему проблему ... скорректировал мойэкспортировать код, чтобы распечатать SQL-код, близкий к нему ... затем запустил 5-10 вставок sql вокруг проблемы sql и сумел точно определить проблемный, увидеть строку, которая была слишком длинной, увеличить размер этого столбца и затем большой импортфайл запустился без проблем.

Немного взлома и обходного пути, но когда у вас остается очень мало выбора, вы делаете то, что можете.

2 голосов
/ 24 сентября 2018

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, чтобы заменить идентификатор сообщения 8152 на 2628 либо на уровне сеанса, либо на уровне сервера.

Обратите внимание, что на данный момент, даже в 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'.

Новый идентификатор сообщения - 2628. Это сообщение заменяет сообщение 8152 в любом выводе ошибки, если включен флаг трассировки 460.

дБ <> демонстрация скрипки

1 голос
/ 19 июля 2016

Я создал хранимую процедуру, которая анализирует исходную таблицу или запрос с несколькими характеристиками на столбец, среди которых минимальная длина (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<br> 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,<br> 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,<br> customer_id NULL NULL NULL NULL 4 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> email NULL NULL NULL NULL 5 NULL YES varchar 100 NULL NULL 0 4 36 0 0 0 31 Many (31)<br> mobile NULL NULL NULL NULL 6 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> initials NULL NULL NULL NULL 7 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> title_short NULL NULL NULL NULL 8 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> title_long NULL NULL NULL NULL 9 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> firstname NULL NULL NULL NULL 10 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> lastname NULL NULL NULL NULL 11 NULL YES varchar 50 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> address NULL NULL NULL NULL 12 NULL YES varchar 100 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> pc NULL NULL NULL NULL 13 NULL YES varchar 10 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> kixcode NULL NULL NULL NULL 14 NULL YES varchar 20 NULL NULL 0 NULL NULL 70 0 0 0 NULL all null,empty,<br> 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,<br> 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,<br> 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,<br> optin NULL NULL NULL NULL 19 NULL YES int NULL 10 10 0 1 1 39 0 31 2 0,1 same length,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...