SQL Server-Zeichenfolge oder Binärdaten werden abgeschnitten

149

Ich bin an einem Datenmigrationsprojekt beteiligt. Beim Versuch, Daten aus einer Tabelle in eine andere Tabelle einzufügen (SQL Server 2005), wird folgende Fehlermeldung angezeigt:

Meldung 8152, Ebene 16, Status 13, Zeile 1
Zeichenfolge oder Binärdaten werden abgeschnitten.

Die Quelldatenspalten stimmen mit dem Datentyp überein und befinden sich innerhalb der Längendefinitionen der Zieltabellenspalten, sodass ich nicht weiß, was diesen Fehler verursachen könnte.

Jim Evans
quelle
Würde es Ihnen etwas ausmachen, Code und Informationen zu jeder Tabelle zu veröffentlichen?
Kevin Mansel
Die Tabellen sind beide ziemlich groß - daher werde ich nur den Teil der beteiligten Tabellendefinitionen und den Code veröffentlichen - ist das akzeptabel?
Jim Evans
Die Tabellendefinitionen und der Code wären großartig.
IAmTimCorey
1
Das letzte Mal, als ich dieses Problem hatte, war es mit dem Auslöser. Der Auslöser fügte Daten in eine Prüftabelle ein. Es lohnt sich auch, den Auslöser zu überprüfen.
Sachin Vishwakarma

Antworten:

185

Sie müssen die Tabellendefinitionen für die Quell- und Zieltabellen veröffentlichen, damit wir herausfinden können, wo das Problem liegt. Unter dem Strich ist jedoch eine Ihrer Spalten in der Quelltabelle größer als Ihre Zielspalten . Es kann sein, dass Sie Formate auf eine Weise ändern, die Sie nicht kannten. Das Datenbankmodell, von dem Sie wechseln, ist ebenfalls wichtig, um dies herauszufinden.

IAmTimCorey
quelle
1
Laut meinem Kommentar oben - in Kürze :)
Jim Evans
3
Ich hatte das gleiche Problem und musste alle Spaltentypen und -größen beider Tabellen vergleichen, um das Problem zu beheben.
Aziz Shaikh
1
Nachdem ich mich die Mühe gemacht hatte, die partiellen Tabellendefinitionen zu sammeln und dann meinen Sproc-Code zu erhalten, sprang die beleidigende Spalte wie ein Blitz auf mich zu ... Vielen Dank für Ihre Eingabe.
Jim Evans
Ich kann dir nicht sagen, wie oft ich das Gleiche getan habe. Ich bin froh, dass Sie Ihr Problem lösen konnten.
IAmTimCorey
Ich habe Ihre erste Antwort als Antwort markiert, weil ich die Antwort gefunden habe :)
Jim Evans
86

Wie bereits erwähnt, ist einer Ihrer Spaltendatentypen in der Quelltabelle größer als Ihre Zielspalten.

Eine einfache Lösung besteht darin, die Warnung einfach auszuschalten und das Abschneiden zuzulassen. Wenn Sie diesen Fehler erhalten, aber sicher sind, dass Daten in Ihrer alten Datenbank / Tabelle abgeschnitten (zugeschnitten) werden, können Sie einfach Folgendes tun:

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

Denken Sie wie oben immer daran, die Warnungen danach wieder einzuschalten. Ich hoffe das hilft.

Rudi Kershaw
quelle
1
Du hast mir einige Stunden Arbeit erspart! Nimm meinen ganzen Dank!
Urasquirrel
Ebenso hier. Manchmal muss ich Daten in einer Tabelle speichern, beispielsweise von einem Webdienst, bei dem der Datentyp nur als "Zeichenfolge" definiert ist. Ich kann nicht alles zu einem Varchar machen (MAX) ...
Curt
61

Das Problem ist ganz einfach: Eine oder mehrere der Spalten in der Quellabfrage enthalten Daten, die die Länge der Zielspalte überschreiten. Eine einfache Lösung wäre, Ihre Quellabfrage zu nehmen und Max(Len( source col ))für jede Spalte auszuführen . Dh

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

Vergleichen Sie dann diese Längen mit den Datentyplängen in Ihrer Zieltabelle. Mindestens eine überschreitet die Zielspaltenlänge.

Wenn Sie absolut sicher sind, dass dies nicht der Fall sein sollte und es egal ist, ob dies nicht der Fall ist , besteht eine andere Lösung darin, die Quellabfragespalten zwangsweise auf ihre Ziellänge umzustellen (wodurch zu lange Daten abgeschnitten werden):

Select Cast(TextCol1 As varchar(...))
    , Cast(TextCol2 As varchar(...))
    , Cast(TextCol3 As varchar(...))
    , ...
From ...
Thomas
quelle
Mein täglicher Prozess begann mit diesem Fehler zu brechen. Die Daten, die ich einfüge, waren immer kurz genug, um zu passen, und ich hatte immer andere Zeilen (in der Tabelle, aus der ich gezogen habe) mit übergroßen Zeichenfolgen, die aufgrund meines Filters nie eingefügt wurden. Vielleicht wurde ein Index neu erstellt oder die Statistik wurde aktualisiert, aber der Geist in der Maschine entschied eines Tages, dass ihm der Abfrageplan nicht mehr gefiel, weil er einen Pfad entlangführte, auf dem Daten (die zu breit waren) "sein" konnten eingefügt, bevor es vom Prädikat in der Where-Klausel gefiltert wurde. Um dies zu umgehen, habe ich LEFT () anstelle von CAST verwendet - nur weniger Zeichen zum Eingeben.
MikeTeeVee
1
Danke Thomas, das ist seltsam, auch wenn ich keine zu langen Daten habe, muss ich sie trotzdem auf die neue Zielspaltengröße umwandeln, sobald ich das getan habe.
Michelle
15

SQL Server 2019 gibt endlich eine aussagekräftigere Fehlermeldung zurück.

Binär- oder Zeichenfolgendaten werden abgeschnitten => Verbesserungen der Fehlermeldung

Wenn Sie diesen Fehler haben (in der Produktion), ist es nicht offensichtlich, aus welcher Spalte oder Zeile dieser Fehler stammt und wie Sie ihn genau finden.

Um neues Verhalten zu aktivieren, müssen Sie verwenden DBCC TRACEON(460). Neuer Fehlertext von sys.messages:

SELECT * FROM sys.messages WHERE message_id = 2628

2628 - Zeichenfolge oder Binärdaten werden in der Tabelle '%. * Ls', Spalte '%. * Ls' abgeschnitten. Abgeschnittener Wert: '%. * Ls'.

Zeichenfolgen- oder Binärdaten würden abgeschnitten: Ersetzen des berüchtigten Fehlers 8152

Diese neue Nachricht wird auch auf SQL Server 2017 CU12 (und in einer kommenden SQL Server 2016 SP2-CU) zurückportiert, jedoch nicht standardmäßig. Sie müssen das Ablaufverfolgungsflag 460 aktivieren, um die Nachrichten-ID 8152 entweder auf Sitzungs- oder Serverebene durch 2628 zu ersetzen.

Beachten Sie, dass vorerst auch in SQL Server 2019 CTP 2.0 das gleiche Ablaufverfolgungsflag 460 aktiviert sein muss. In einer zukünftigen Version von SQL Server 2019 ersetzt die Nachricht 2628 standardmäßig die Nachricht 8152.


SQL Server 2017 CU12 unterstützt diese Funktion ebenfalls.

Verbesserung: Optionaler Ersatz für die Meldung "Zeichenfolge oder Binärdaten werden abgeschnitten" mit erweiterten Informationen in SQL Server 2017

Dieses SQL Server 2017-Update führt eine optionale Nachricht ein, die die folgenden zusätzlichen Kontextinformationen enthält.

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

Die neue Nachrichten-ID lautet 2628. Diese Nachricht ersetzt die Nachricht 8152 in jeder Fehlerausgabe, wenn das Ablaufverfolgungsflag 460 aktiviert ist.

db <> Geigen-Demo


ALTER DATABASE SCOPED CONFIGURATION

VERBOSE_TRUNCATION_WARNINGS = {ON | AUS }

GILT FÜR: SQL Server (ab SQL Server 2019 (15.x)) und Azure SQL-Datenbank

Ermöglicht das Aktivieren oder Deaktivieren der neuen Zeichenfolge. SQL Server 2019 (15.x) führt eine neue, spezifischere Fehlermeldung (2628) für dieses Szenario ein:

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

Bei Kürzung unter Datenbankkompatibilitätsstufe 150 lösen Kürzungsfehler die neue Fehlermeldung 2628 aus, um mehr Kontext bereitzustellen und den Fehlerbehebungsprozess zu vereinfachen.

Wenn unter Datenbankkompatibilitätsstufe 150 auf AUS gesetzt, lösen Kürzungsfehler die vorherige Fehlermeldung 8152 aus.

Für die Datenbankkompatibilitätsstufe 140 oder niedriger bleibt die Fehlermeldung 2628 eine Opt-In-Fehlermeldung, für die das Trace-Flag 460 aktiviert werden muss, und diese Konfiguration mit Datenbankbereich hat keine Auswirkung.

Lukasz Szozda
quelle
1
Dies ist jetzt auch für SQL Azure verfügbar: azure.microsoft.com/en-gb/updates/…
Ian Kemp
7

Ein weiterer möglicher Grund hierfür ist, dass Sie einen Standardwert für eine Spalte eingerichtet haben, der die Länge der Spalte überschreitet. Es sieht so aus, als hätte jemand eine Spalte mit einer Länge von 5 gefingert, aber der Standardwert überschritt die Länge von 5. Dies machte mich verrückt, als ich zu verstehen versuchte, warum es bei keinem Einsatz funktionierte, selbst wenn alles, was ich einfügte, war Eine einzelne Spalte mit einer Ganzzahl von 1. Da der Standardwert im Tabellenschema diesen verletzenden Standardwert hatte, hat dies alles durcheinander gebracht - was uns zu der gewonnenen Erkenntnis bringt -, vermeiden Sie es, Tabellen mit Standardwerten im Schema zu haben. :) :)

Brian
quelle
1
Ich denke nicht, dass das Vermeiden von Standardwerten eine gute Lösung ist. Standardwerte sind sehr nützlich. Ich würde Datenbank "Probleme", die durch Tippfehler verursacht werden, nicht durch Entfernen der Standardwerte lösen ...
Jacob H
3

Überprüfen Sie für die anderen auch Ihre gespeicherte Prozedur . In meinem Fall habe CustomSearchich in meiner gespeicherten Prozedur versehentlich nicht genügend Länge für meine Spalte angegeben. Bei der Eingabe von Big Data habe ich diesen Fehler erhalten, obwohl meine Datenbank eine große Länge enthält. Ich habe gerade die Länge meiner Spalte in meiner benutzerdefinierten Suche geändert. Der Fehler verschwindet. Dies ist nur zur Erinnerung. Vielen Dank.

bot
quelle
Genau das passiert mir. Die Quell- / Zieltabellen stimmen gut überein, aber der gespeicherte Prozess hatte eine # -Tabelle mit kürzerer Länge definiert und ist dort fehlgeschlagen. Danke dir!
Joy Walker
3

Dies kann ein herausfordernder Fehler sein. Hier sind einige Notizen von https://connect.microsoft.com/SQLServer/feedback/details/339410/ Folgenden Suchen Sie nach AmirCharanias Kommentar.

Ich habe die Antwort von AmirCharania für Daten angepasst, die in einer tatsächlichen Tabelle anstelle einer temporären Tabelle ausgewählt wurden. Wählen Sie zuerst Ihren Datensatz in einer Entwicklungstabelle aus und führen Sie dann Folgendes aus:

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
quelle
Es sieht so aus, als hätte MS die Connect-Site heruntergefahren. Der neue Link zu diesem Problem lautet: feedback.azure.com/forums/908035-sql-server/suggestions/… ... immer noch als ungeplant markiert. Ich denke, der Kommentar, auf den Sie sich beziehen, wurde (ironischerweise) abgeschnitten, als die Migration stattfand.
SWalters
Interessanterweise wurde die Ausgabe unter einem etwas anderen Titel erneut geöffnet: feedback.azure.com/forums/908035-sql-server/suggestions/… und sie wurde als "Under Review" aufgeführt, sodass noch Hoffnung besteht.
SWalters
2

Ich bin heute auf dieses Problem gestoßen und habe auf meiner Suche nach einer Antwort auf diese minimale informative Fehlermeldung auch diesen Link gefunden:

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 hat also anscheinend keine Pläne, die Fehlermeldung bald zu erweitern.

Also wandte ich mich anderen Mitteln zu.

Ich habe die Fehler kopiert, um sie zu übertreffen:

(1 Zeile (n) betroffen)

(1 Zeile (n) betroffen)

(1 Zeile (n) betroffen) Nachricht 8152, Ebene 16, Status 14, Zeile 13 Zeichenfolge oder Binärdaten werden abgeschnitten. Die Anweisung wurde beendet.

(1 Zeile (n) betroffen)

zählte die Anzahl der Zeilen in Excel, näherte sich dem Datensatzzähler, der das Problem verursacht hatte ... passte meinen Exportcode an, um die SQL in der Nähe auszudrucken ... und führte dann die 5 - 10 SQL-Einfügungen um das Problem SQL und aus Es gelang, das Problem zu lokalisieren, die zu lange Zeichenfolge zu sehen, die Größe dieser Spalte zu erhöhen und dann die große Importdatei ohne Probleme auszuführen.

Ein bisschen wie ein Hack und eine Problemumgehung, aber wenn Sie mit sehr wenig Auswahl abgereist sind, tun Sie, was Sie können.

Shaakir
quelle
2

Ja, ich stehe auch vor solchen Problemen.

REMARKS VARCHAR(500)
to
REMARKS VARCHAR(1000)

Hier habe ich die Länge der BEMERKUNGEN von 500 auf 1000 geändert

Thivan Mydeen
quelle
2

Ich werde eine weitere mögliche Ursache für diesen Fehler hinzufügen, nur weil niemand ihn erwähnt hat und er möglicherweise einer zukünftigen Person hilft (da das OP seine Antwort gefunden hat). Wenn die Tabelle, in die Sie einfügen, Trigger enthält, kann dies sein, dass der Trigger den Fehler generiert. Ich habe dies gesehen, als Tabellenfelddefinitionen geändert wurden, Audittabellen jedoch nicht.

HLGEM
quelle
2

Ja - "ein Pint in einen halben Pint Topf geht nicht". Ich hatte nicht viel Glück (aus welchem ​​Grund auch immer) mit den verschiedenen SPs, die die Leute vorgeschlagen haben, ABER solange sich die beiden Tabellen in derselben Datenbank befinden (oder Sie sie in dieselbe Datenbank bringen können), können Sie INFORMATION_SCHEMA verwenden. SPALTEN, um die fehlerhaften Felder zu lokalisieren, also:

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

Auf diese Weise können Sie nach oben und unten scrollen und dabei die Feldlängen vergleichen. In den kommentierten Abschnitten können Sie (natürlich einmal unkommentiert) sehen, ob Datentypinkongruenzen vorliegen, oder speziell diejenigen anzeigen, die sich in der Feldlänge unterscheiden - weil ich zu faul bin, um zu scrollen -, aber beachten Sie, dass das Ganze auf der Quelle basiert Spaltennamen, die mit denen des Ziels übereinstimmen.

Kevin Anderson
quelle
Ich wollte so etwas schreiben, aber du hast es einfach gemacht. sehr praktisch und wirkte wie ein Zauber. Ich konnte damit eine Tabelle mit mehr als 90 Spalten vergleichen und zwei davon sprangen sofort heraus. Danke dir!
Joy Walker
2

Hier ist eine etwas andere Antwort. Ihre Spaltennamen und -längen stimmen möglicherweise alle überein, aber möglicherweise geben Sie die Spalten in Ihrer SELECT-Anweisung in der falschen Reihenfolge an. Angenommen, tableX und tableY haben Spalten mit demselben Namen, jedoch in unterschiedlicher Reihenfolge

SoloPilot
quelle
1

Ich habe bei der Tabellenerstellung eine leere Zeichenfolge '' verwendet und dann bei der nachfolgenden Aktualisierung den Fehler 'Nachricht 8152, Zeichenfolge oder Binärdaten werden abgeschnitten' erhalten. Dies geschah aufgrund des Aktualisierungswerts, der 6 Zeichen enthielt und größer als die erwartete Spaltendefinition war. Ich habe "SPACE" verwendet, um dies zu umgehen, nur weil ich wusste, dass ich nach der ersten Datenerstellung in großen Mengen aktualisieren würde, dh die Spalte würde nicht lange leer bleiben.

SO GROSSE CAVEAT HIER: Dies ist keine besonders raffinierte Lösung, aber nützlich, wenn Sie einen Datensatz zusammenstellen, z. B. für einmalige Geheimdienstanforderungen, bei denen Sie eine Tabelle für das Data Mining erstellen, eine Massenverarbeitung / -interpretation anwenden und Speichern vor und nach den Ergebnissen für einen späteren Vergleich / Abbau. Dies ist ein häufiges Ereignis in meiner Branche.

Sie können zunächst mit dem Schlüsselwort SPACE füllen, d. H.

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

Nachfolgende Aktualisierungen von "Spaltenname" mit maximal 10 Zeichen (ggf. Ersatz) sind dann zulässig, ohne dass ein Fehler beim Abschneiden verursacht wird. Auch hier würde ich dies nur in Szenarien verwenden, die den in meinem Vorbehalt beschriebenen ähnlich sind.

Hilary
quelle
1

Ich habe eine gespeicherte Prozedur erstellt, die eine Quelltabelle oder Abfrage mit mehreren Merkmalen pro Spalte analysiert, darunter die minimale Länge (min_len) und die maximale Länge (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

Ich speichere diese Prozedur in der Master-Datenbank, damit ich sie in jeder Datenbank wie folgt verwenden kann:

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

Und die Ausgabe ist:

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
quelle
Randbemerkung: Sie sollten nicht die Verwendung sp_Präfix für gespeicherte Prozeduren. Microsoft hat dieses Präfix für den eigenen Gebrauch reserviert (siehe Benennen gespeicherter Prozeduren ) , und Sie laufen Gefahr, irgendwann in der Zukunft einen Namenskonflikt zu erleiden. Dies ist auch schlecht für die Leistung Ihrer gespeicherten Prozedur . Es ist am besten, einfach sp_etwas anderes zu vermeiden und als Präfix zu verwenden - oder überhaupt kein Präfix!
marc_s
1

Ich habe eine nützliche Speicherprozedur geschrieben, um das Problem der Textabschneidung (Zeichenfolge oder Binärdaten werden abgeschnitten) zu identifizieren und zu beheben, wenn die INSERT SELECT-Anweisung verwendet wird. Es vergleicht nur die Felder CHAR, VARCHAR, NCHAR UND NVARCHAR und gibt Feld für Feld eine Auswertung zurück, falls dies die mögliche Fehlerursache ist.

EXEC dbo.GetFieldStringTruncate SourceTableName, TargetTableName

Diese gespeicherte Prozedur ist auf das Problem der Textabschneidung ausgerichtet, wenn eine INSERT SELECT-Anweisung ausgeführt wird.

Der Betrieb dieser gespeicherten Prozedur hängt davon ab, dass der Benutzer zuvor die INSERT-Anweisung mit dem Problem identifiziert hat. Fügen Sie dann die Quelldaten in eine globale temporäre Tabelle ein. Die Anweisung SELECT INTO wird empfohlen.

Sie müssen im Alias ​​jedes Felds der SELECT-Anweisung denselben Namen für das Feld der Zieltabelle verwenden.

FUNKTIONSCODE:

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

Unterstützt derzeit nur die Datentypen CHAR, VARCHAR, NCHAR und NVARCHAR . Die letzte Version dieses Codes finden Sie im nächsten Link unten. Wir helfen uns gegenseitig, ihn zu verbessern. GetFieldStringTruncate.sql

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

JotaPardo
quelle
1

Wenn Sie mit SQL Server 2016-2017 arbeiten: Aktivieren Sie das Trace-Flag 460, um das Problem zu beheben

DBCC TRACEON(460, 1);
GO

und stellen Sie sicher, dass Sie es ausschalten, nachdem:

DBCC TRACEOFF(460, 1);
GO

Quelle

nimajv
quelle
0

Dies kann auch passieren, wenn Sie nicht über ausreichende Berechtigungen verfügen

Keks128
quelle
2
"Ja wirklich?" Ein tatsächlicher Fehler "Zeichenfolge oder Binärdaten würden abgeschnitten"? Das scheint ein sehr seltsamer Fehler zu sein, wenn Sie keine Berechtigungen haben. Gibt es eine Erlaubnis, die Sie daran hindert, mehr als eine bestimmte Datenmenge zu schreiben? (Ich bin interessiert, weil ich automatisch nach der Feldgröße suchen möchte, wenn ich diesen Fehler erhalte - wenn es also aus einem anderen Grund auftreten könnte, ist das sehr interessant!)
Ian Grainger
0

Ich hatte ein ähnliches Problem. Ich habe Daten von einer Tabelle in eine identische Tabelle in allem außer dem Namen kopiert.

Schließlich habe ich die Quelltabelle mit einer SELECT INTO-Anweisung in eine temporäre Tabelle geschrieben.

SELECT *
INTO TEMP_TABLE
FROM SOURCE_TABLE;

Ich habe das Schema der Quelltabelle mit der temporären Tabelle verglichen. Ich fand eine der Spalten eine, varchar(4000)als ich eine erwartetevarchar(250) .

UPDATE: Das Problem varchar (4000) kann hier erklärt werden, falls Sie interessiert sind:

Für Nvarchar (Max) bekomme ich nur 4000 Zeichen in TSQL?

Hoffe das hilft.

Warren Banken
quelle
0

Dieser Fehler wird ausgelöst, wenn die Spalte einer Tabelle eine Einschränkung [meistens Länge] enthält. . Wenn beispielsweise das Datenbankschema für die Spalte myColumn CHAR (2) ist, müssen Sie beim Aufruf einer Anwendung zum Einfügen eines Werts einen String der Länge zwei übergeben.

Der Fehler sagt es im Grunde; Zeichenfolge mit der Länge drei und höher ist inkonsistent, um der im Datenbankschema angegebenen Längenbeschränkung zu entsprechen. Aus diesem Grund warnt SQL Server vor Datenverlust / Kürzungsfehler.

Yergalem
quelle
0

Bitte versuchen Sie den folgenden Code:

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
quelle