SQL Server String lub dane binarne zostałyby obcięte

149

Jestem zaangażowany w projekt migracji danych. Otrzymuję następujący błąd, gdy próbuję wstawić dane z jednej tabeli do innej tabeli (SQL Server 2005):

Msg 8152, poziom 16, stan 13, wiersz 1
Ciąg lub dane binarne zostałyby obcięte.

Źródłowe kolumny danych są zgodne z typem danych i mieszczą się w definicjach długości kolumn tabeli docelowej, więc nie wiem, co może powodować ten błąd.

Jim Evans
źródło
Czy mógłbyś opublikować jakiś kod i informacje o każdej tabeli?
Kevin Mansel
Obie tabele są dość duże - więc opublikuję tylko część definicji tabeli, które są zaangażowane, oraz kod - czy to jest akceptowalne?
Jim Evans,
Definicje tabel i kod byłyby świetne.
IAmTimCorey
1
Ostatni raz, kiedy miałem ten problem, był to wyzwalacz, wyzwalacz wstawiał dane do tabeli audytu. Warto również sprawdzić spust.
Sachin Vishwakarma

Odpowiedzi:

185

Będziesz musiał opublikować definicje tabeli dla tabel źródłowych i docelowych, abyśmy mogli ustalić, gdzie jest problem, ale najważniejsze jest to, że jedna z kolumn w tabeli źródłowej jest większa niż kolumny docelowe . Możliwe, że zmieniasz formaty w sposób, którego nie byłeś świadomy. Model bazy danych, z którego się przenosisz, jest również ważny przy określaniu tego.

IAmTimCorey
źródło
1
Na mój komentarz powyżej - już wkrótce :)
Jim Evans
3
Miałem ten sam problem i musiałem porównać wszystkie typy kolumn i rozmiary obu tabel, aby rozwiązać problem.
Aziz Shaikh
1
Po przejściu przez egzekucję zebrania częściowych definicji tabeli, a następnie otrzymaniu mojego kodu SPRO, obraźliwa kolumna wyskoczyła na mnie jak błyskawica ... Dziękuję wszystkim za wkład.
Jim Evans,
Nie mogę ci powiedzieć, ile razy zrobiłem to samo. Cieszę się, że udało Ci się rozwiązać problem.
IAmTimCorey
Oznaczyłem Twoją pierwszą odpowiedź jako odpowiedź, ponieważ to właśnie doprowadziło mnie do znalezienia odpowiedzi :)
Jim Evans
86

Jak już powiedzieli inni, jeden z typów danych kolumn w tabeli źródłowej jest większy niż kolumny docelowe.

Prostym rozwiązaniem jest po prostu wyłączenie ostrzeżenia i zezwolenie na obcięcie. Jeśli więc otrzymujesz ten błąd, ale masz pewność, że obcięcie (przycięcie do rozmiaru) danych w starej bazie danych / tabeli jest dopuszczalne, możesz po prostu wykonać następujące czynności;

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

Jak powyżej, zawsze pamiętaj, aby później ponownie włączyć ostrzeżenia. Mam nadzieję, że to pomoże.

Rudi Kershaw
źródło
1
Zaoszczędziłeś mi kilka godzin pracy! Przyjmij wszystkie moje podziękowania!
Urasquirrel
Podobnie tutaj. Czasami muszę przechowywać dane w tabeli z, powiedzmy, usługi internetowej, w której typ danych jest definiowany tylko jako „ciąg znaków”. Nie mogę zrobić ze wszystkiego Varchar (MAX) ...
Curt,
61

Problem jest dość prosty: co najmniej jedna kolumna w zapytaniu źródłowym zawiera dane, które przekraczają długość kolumny docelowej. Prostym rozwiązaniem byłoby pobranie zapytania źródłowego i wykonanie go Max(Len( source col ))w każdej kolumnie. To znaczy,

Select Max(Len(TextCol1))
    , Max(Len(TextCol2))
    , Max(Len(TextCol3))
    , ...
From ...

Następnie porównaj te długości z długościami typów danych w tabeli docelowej. Co najmniej jeden, przekracza długość swojej kolumny docelowej.

Jeśli masz absolutną pewność, że tak nie jest i nie obchodzi Cię, czy tak nie jest , innym rozwiązaniem jest wymuszenie rzutowania kolumn zapytania źródłowego na ich docelową długość (co spowoduje obcięcie wszelkich danych, które są zbyt długie):

Select Cast(TextCol1 As varchar(...))
    , Cast(TextCol2 As varchar(...))
    , Cast(TextCol3 As varchar(...))
    , ...
From ...
Tomasz
źródło
Mój codzienny proces zaczął się zepsuć z powodu tego błędu. Dane, które wstawiłem, były zawsze wystarczająco krótkie, aby się zmieścić i zawsze miałem inne wiersze (w tabeli, z której wyciągałem) z ponadwymiarowymi ciągami, które nigdy nie zostały wstawione z powodu mojego filtra. Może indeks został przebudowany lub statystyki zostały zaktualizowane, ale duch w maszynie zdecydował pewnego dnia, że ​​plan zapytania już mu się nie podoba, ponieważ sprowadził go na ścieżkę, na której dane (które były zbyt szerokie) „mogły” być wstawiony przed przefiltrowaniem przez orzeczenie w klauzuli Where. Aby obejść ten problem, użyłem LEFT () zamiast CAST - tylko mniej znaków do wpisania.
MikeTeeVee
1
Dziękuję Thomas, to dziwne, nawet ja nie mam żadnych danych, które są zbyt długie, nadal muszę rzutować je na nowy rozmiar kolumny docelowej, jak tylko to zrobiłem, zadziałało.
Michelle
15

SQL Server 2019 w końcu zwróci bardziej znaczący komunikat o błędzie.

Dane binarne lub ciągowe zostaną obcięte => ulepszenia komunikatów o błędach

jeśli masz ten błąd (w środowisku produkcyjnym), nie jest oczywiste, z której kolumny lub wiersza pochodzi ten błąd i jak dokładnie go zlokalizować.

Aby włączyć nowe zachowanie, musisz użyć DBCC TRACEON(460). Nowy tekst błędu od sys.messages:

SELECT * FROM sys.messages WHERE message_id = 2628

2628 - Dane ciągowe lub binarne zostaną obcięte w tabeli „%. * Ls”, kolumnie „%. * Ls”. Obcięta wartość: „%. * Ls”.

Dane ciągowe lub binarne zostaną obcięte: zastąpienie niesławnego błędu 8152

Ta nowa wiadomość jest również przenoszona do SQL Server 2017 CU12 (i w nadchodzącej SQL Server 2016 SP2 CU), ale nie domyślnie. Musisz włączyć flagę śledzenia 460, aby zastąpić identyfikator komunikatu 8152 2628, na poziomie sesji lub serwera.

Zauważ, że na razie nawet w SQL Server 2019 CTP 2.0 ta sama flaga śledzenia 460 musi być włączona. W przyszłej wersji programu SQL Server 2019 komunikat 2628 zastąpi domyślnie komunikat 8152.


SQL Server 2017 CU12 obsługuje również tę funkcję.

Ulepszenie: Opcjonalne zastąpienie komunikatu „Ciąg lub dane binarne zostaną obcięte” z rozszerzonymi informacjami w programie SQL Server 2017

Ta aktualizacja programu SQL Server 2017 wprowadza opcjonalny komunikat zawierający następujące dodatkowe informacje kontekstowe.

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

Nowy identyfikator komunikatu to 2628. Ten komunikat zastępuje komunikat 8152 w dowolnym wyniku błędu, jeśli flaga śledzenia 460 jest włączona.

db <> fiddle demo


ZMIANA KONFIGURACJI ZAKRESU BAZY DANYCH

VERBOSE_TRUNCATION_WARNINGS = {ON | OFF}

DOTYCZY: SQL Server (począwszy od SQL Server 2019 (15.x)) i Azure SQL Database

Umożliwia włączenie lub wyłączenie nowego komunikatu o błędzie typu ciąg lub dane binarne. SQL Server 2019 (15.x) wprowadza nowy, bardziej szczegółowy komunikat o błędzie (2628) dla tego scenariusza:

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

Po ustawieniu na ON na poziomie zgodności bazy danych 150, błędy obcięcia powodują pojawienie się nowego komunikatu o błędzie 2628, aby zapewnić większy kontekst i uprościć proces rozwiązywania problemów.

Po ustawieniu na WYŁ. Na poziomie zgodności bazy danych 150, błędy obcięcia powodują poprzedni komunikat o błędzie 8152.

W przypadku poziomu zgodności bazy danych 140 lub niższego komunikat o błędzie 2628 pozostaje komunikatem o błędzie akceptacji, który wymaga włączenia flagi śledzenia 460, a ta konfiguracja zakresu bazy danych nie ma żadnego wpływu.

Łukasz Szozda
źródło
1
Jest to teraz dostępne również dla SQL Azure: azure.microsoft.com/en-gb/updates/ ...
Ian Kemp,
7

Innym potencjalnym powodem jest to, że masz domyślną konfigurację wartości dla kolumny, która przekracza długość kolumny. Wygląda na to, że ktoś gruby dotknął palcem kolumny o długości 5, ale domyślna wartość przekraczała długość 5. To doprowadziło mnie do szału, ponieważ próbowałem zrozumieć, dlaczego nie działa na żadnej wkładce, nawet jeśli wszystko, co wstawiałem, to pojedyncza kolumna z liczbą całkowitą równą 1. Ponieważ domyślna wartość w schemacie tabeli miała tę naruszającą wartość domyślną, wszystko zepsuło - co, jak sądzę, prowadzi nas do nauczonej lekcji - unikaj umieszczania w schemacie tabel z wartością domyślną. :)

Brian
źródło
1
Uważam, że unikanie wartości domyślnych nie jest dobrym rozwiązaniem. Wartości domyślne są bardzo przydatne. Nie rozwiązałbym "problemów" z bazą danych spowodowanych literówkami poprzez usunięcie wartości domyślnych ...
Jacob H
3

W przypadku pozostałych sprawdź również swoją procedurę składowaną . W moim przypadku w mojej procedurze składowanej CustomSearchprzypadkowo zadeklarowałem za małą długość dla mojej kolumny, więc kiedy wprowadziłem duże zbiory danych, otrzymałem ten błąd, mimo że mam dużą długość w mojej bazie danych. Właśnie zmieniłem długość mojej kolumny w wyszukiwaniu niestandardowym, błąd zniknął. To jest tylko dla przypomnienia. Dzięki.

nerw
źródło
to jest dokładnie to, co się ze mną dzieje. tabele źródłowe / docelowe pasują dobrze, ale przechowywany proc miał zdefiniowaną tabelę # o krótszej długości i tam zawiodło. Dziękuję Ci!
Joy Walker
3

Może to być trudny błąd. Oto kilka notatek z https://connect.microsoft.com/SQLServer/feedback/details/339410/ poszukaj komentarza AmirCharania.

Dostosowałem odpowiedź udzieloną przez AmirCharania dla danych wybranych do rzeczywistej tabeli zamiast tymczasowej. Najpierw wybierz zestaw danych do tabeli programistycznej, a następnie uruchom następujące czynności:

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)
mcfea
źródło
Wygląda na to, że firma MS zamknęła witrynę Connect. Nowy link do tego problemu to: feedback.azure.com/forums/908035-sql-server/suggestions/ ... ... nadal oznaczony jako nieplanowany. Myślę, że komentarz, do którego się odnosisz, został (jak na ironię) obcięty, gdy nastąpiła migracja.
SWalters - Przywróć Monikę
Co ciekawe, sprawa została ponownie otwarta pod nieco innym tytułem: feedback.azure.com/forums/908035-sql-server/suggestions/ ... i została oznaczona jako „W trakcie sprawdzania”, więc jest jeszcze nadzieja.
SWalters - Przywróć Monikę
3

Oto nieco inna odpowiedź. Wszystkie nazwy i długości kolumn mogą się zgadzać, ale być może w instrukcji SELECT określasz kolumny w złej kolejności. Powiedzmy, że tabela X i tabela Y mają kolumny o tej samej nazwie, ale w innej kolejności

SoloPilot
źródło
2

Dzisiaj natknąłem się na ten problem, a szukając odpowiedzi na ten minimalny informacyjny komunikat o błędzie, znalazłem również ten link:

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

Wygląda więc na to, że Microsoft nie planuje w najbliższym czasie rozwinąć komunikatu o błędzie.

Więc zwróciłem się do innych środków.

Skopiowałem błędy do excela:

(Dotyczy 1 wierszy)

(Dotyczy 1 wierszy)

(Dotyczy 1 wierszy) Msg 8152, poziom 16, stan 14, wiersz 13 Dane ciągowe lub binarne zostałyby obcięte. Oświadczenie zostało zakończone.

(Dotyczy 1 wierszy)

policzyłem liczbę wierszy w programie Excel, zbliżyłem się do licznika rekordów, który spowodował problem ... dostosowałem mój kod eksportu, aby wydrukować najbliższy kod SQL ... następnie uruchomiłem wstawki 5-10 sql wokół problemu sql i udało się wskazać problem pierwszy, zobaczyć ciąg, który był zbyt długi, zwiększyć rozmiar tej kolumny, a następnie duży plik importu nie działał.

Trochę hackowania i obejścia, ale kiedy wyszedłeś z bardzo małym wyborem, robisz, co możesz.

Shaakir
źródło
2

Tak, ja też mam takie problemy.

REMARKS VARCHAR(500)
to
REMARKS VARCHAR(1000)

Tutaj zmieniłem długość wpisanego UWAGI z 500 na 1000

Thivan Mydeen
źródło
2

Dodam jeszcze jedną możliwą przyczynę tego błędu tylko dlatego, że nikt o tym nie wspomniał i może to pomóc jakiejś przyszłej osobie (skoro PO znalazł odpowiedź). Jeśli tabela, do której wstawiasz, ma wyzwalacze, może to być wyzwalacz generujący błąd. Widziałem, jak to się dzieje, kiedy definicje pól tabeli zostały zmienione, ale tabele audytu nie.

HLGEM
źródło
2

Tak - „kufel do półlitrowego garnka nie trafi”. Nie miałem dużo szczęścia (z jakiegokolwiek powodu) z różnymi SP, które ludzie sugerowali, ALE tak długo, jak dwie tabele są w tej samej bazie danych (lub możesz je umieścić w tej samej bazie danych), możesz użyć INFORMATION_SCHEMA. KOLUMNY do zlokalizowania błędnego pola (pól), a zatem:

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

Umożliwi to przewijanie w górę iw dół, porównując długości pól w trakcie. Skomentowane sekcje pozwalają zobaczyć (oczywiście po odkomentowaniu), czy występują niezgodności typów danych, lub konkretnie pokazują te, które różnią się długością pola - ponieważ jestem zbyt leniwy, aby przewijać - po prostu pamiętaj, że cała rzecz jest oparta na źródle nazwy kolumn zgodne z docelowymi.

Kevin Anderson
źródło
Miałem napisać coś takiego, ale po prostu to ułatwiłeś. bardzo poręczny i działał jak urok. Udało mi się go użyć do porównania tabeli z ponad 90 kolumnami i dwie z nich od razu wyskoczyły. Dziękuję Ci!
Joy Walker
1

Używałem pustego ciągu „” podczas tworzenia tabeli, a następnie otrzymywałem błąd „Msg 8152, Ciąg lub dane binarne zostaną obcięte” przy kolejnej aktualizacji. Działo się tak z powodu wartości aktualizacji zawierającej 6 znaków i większej niż przewidywana definicja kolumny. Użyłem „SPACJI”, aby obejść ten problem, tylko dlatego, że wiedziałem, że będę aktualizować zbiorczo po utworzeniu początkowych danych, tj. Kolumna nie będzie długo pozostawać pusta.

TAK DUŻA OSTRZEŻENIE: To nie jest szczególnie sprytne rozwiązanie, ale jest przydatne w przypadku, gdy gromadzisz zestaw danych, np. W przypadku jednorazowych żądań wywiadowczych, w których tworzysz tabelę do eksploracji danych, stosując pewne przetwarzanie / interpretację masową i przechowywanie wyników przed i po do późniejszego porównania / wydobycia. To częste zjawisko w mojej pracy.

Możesz początkowo wypełnić słowo kluczowe SPACE, tj

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

Późniejsze aktualizacje parametru „nazwa_kolumny” składającego się z 10 lub mniej znaków (w razie potrzeby należy je zastąpić) będą wtedy dozwolone bez powodowania błędu obcięcia. Ponownie użyłbym tego tylko w scenariuszach podobnych do opisanego w moim zastrzeżeniu.

Hilary
źródło
1

Zbudowałem procedurę składowaną, która analizuje tabelę źródłową lub kwerendę z kilkoma cechami na kolumnę, w tym minimalną długością (min_len) i maksymalną długością (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

Przechowuję tę procedurę w głównej bazie danych, aby móc jej używać w każdej bazie danych, tak jak:

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

A wynik to:

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,

Christiaan Westerbeek
źródło
Dygresja: należy nie używać sp_prefiksu dla procedur składowanych. Firma Microsoft zarezerwowała ten prefiks do własnego użytku (zobacz Procedury składowane nazewnictwa ) i istnieje ryzyko kolizji nazw w przyszłości. Jest to również szkodliwe dla wydajności procedury składowanej . Najlepiej po prostu unikać sp_i używać czegoś innego jako przedrostka - lub nie używać go wcale!
marc_s
1

Napisałem przydatną procedurę przechowywania, aby pomóc zidentyfikować i rozwiązać problem obcinania tekstu (dane ciągi lub dane binarne byłyby obcięte), gdy używana jest instrukcja INSERT SELECT. Porównuje tylko pola CHAR, VARCHAR, NCHAR I NVARCHAR i zwraca ocenę pole po polu w przypadku, gdy jest to możliwa przyczyna błędu.

EXEC dbo.GetFieldStringTruncate SourceTableName, TargetTableName

Ta procedura składowana jest zorientowana na problem obcięcia tekstu, gdy wykonywana jest instrukcja INSERT SELECT.

Działanie tej procedury składowanej zależy od użytkownika, który wcześniej zidentyfikował instrukcję INSERT z problemem. Następnie wstawianie danych źródłowych do globalnej tabeli tymczasowej. Zalecana jest instrukcja SELECT INTO.

Musisz użyć tej samej nazwy pola tabeli docelowej w aliasie każdego pola instrukcji SELECT.

KOD FUNKCJI:

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

Na razie obsługuje tylko typy danych CHAR, VARCHAR, NCHAR i NVARCHAR . Możesz znaleźć ostatnią wersję tego kodu w następnym linku poniżej i pomagamy sobie nawzajem w ulepszaniu go. GetFieldStringTruncate.sql

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

JotaPardo
źródło
1

Jeśli korzystasz z programu SQL Server 2016-2017: aby to naprawić, włącz flagę śledzenia 460

DBCC TRACEON(460, 1);
GO

i upewnij się, że wyłączyłeś go po:

DBCC TRACEOFF(460, 1);
GO

źródło

nimajv
źródło
0

może się to również zdarzyć, gdy nie masz odpowiednich uprawnień

Ciastko128
źródło
2
Naprawdę? Rzeczywisty błąd „Dane ciągowe lub binarne zostałyby obcięte”? Wygląda to na bardzo dziwny błąd, jeśli nie masz uprawnień. Czy istnieje pozwolenie, które powstrzymuje Cię przed zapisaniem większej ilości danych niż określona? (Jestem zainteresowany, ponieważ chcę automatycznie sprawdzić rozmiar pola, gdy otrzymuję ten błąd - więc jeśli może się zdarzyć z innego powodu, to jest to bardzo interesujące!)
Ian Grainger
0

Miałem podobny problem. Kopiowałem dane z jednej tabeli do identycznej tabeli we wszystkim oprócz nazwy.

Ostatecznie zrzuciłem tabelę źródłową do tabeli tymczasowej za pomocą instrukcji SELECT INTO.

SELECT *
INTO TEMP_TABLE
FROM SOURCE_TABLE;

Porównałem schemat tabeli źródłowej z tabelą tymczasową. Odkryłem, że jedna z kolumn była a, varchar(4000)kiedy spodziewałem się pliku varchar(250).

AKTUALIZACJA: Kwestię varchar (4000) można wyjaśnić tutaj, jeśli jesteś zainteresowany:

W przypadku Nvarchar (Max) otrzymuję tylko 4000 znaków w TSQL?

Mam nadzieję że to pomoże.

banki Warren
źródło
0

Ten błąd jest generowany, gdy kolumna tabeli nakłada ograniczenie [głównie długość]. . Np. Jeśli schemat bazy danych dla kolumny myColumn to CHAR (2), to gdy wywołujesz z dowolnej aplikacji, aby wstawić wartość, musisz przekazać ciąg o długości dwa.

Błąd w zasadzie mówi to; ciąg o długości trzy i powyżej jest niespójny i nie pasuje do ograniczenia długości określonego przez schemat bazy danych. Dlatego SQL Server ostrzega i zgłasza błąd utraty / obcięcia danych.

Yergalem
źródło
0

Spróbuj następującego kodu:

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]
arnav
źródło