La chaîne SQL Server ou les données binaires seraient tronquées


149

Je suis impliqué dans un projet de migration de données. J'obtiens l'erreur suivante lorsque j'essaie d'insérer des données d'une table dans une autre table (SQL Server 2005):

Msg 8152, niveau 16, état 13, ligne 1 La
chaîne ou les données binaires seraient tronquées.

Les colonnes de données source correspondent au type de données et sont comprises dans les définitions de longueur des colonnes de la table de destination. Je ne sais donc pas ce qui pourrait causer cette erreur.


Pourriez-vous publier du code et des informations sur chaque table?
Kevin Mansel

Les tables sont toutes les deux assez grandes - donc je ne posterai que la partie des définitions de table qui sont impliquées et le code - est-ce acceptable?
Jim Evans

Les définitions de table et le code seraient super.
IAmTimCorey

1
La dernière fois que j'ai eu ce problème, c'était avec le déclencheur, le déclencheur insérait des données dans une table d'audit. vaut également la peine de vérifier le déclencheur.
Sachin Vishwakarma du

Réponses:


185

Vous devrez publier les définitions de table pour les tables source et de destination pour que nous puissions déterminer où se situe le problème, mais fin de compte, l'une de vos colonnes dans la table source est plus grande que vos colonnes de destination . Il se peut que vous changiez de format d'une manière dont vous n'étiez pas au courant. Le modèle de base de données à partir duquel vous vous déplacez est également important pour comprendre cela.


1
Par mon commentaire ci-dessus - à venir bientôt :)
Jim Evans

3
J'avais rencontré le même problème et j'ai dû comparer tous les types et tailles de colonnes des deux tableaux pour résoudre le problème.
Aziz Shaikh

1
Après avoir parcouru l'exécutif de rassembler les définitions de table partielles, puis avoir obtenu mon code sproc, la colonne incriminée m'a sauté dessus comme un éclair ... Merci à tous pour votre contribution.
Jim Evans

Je ne peux pas vous dire combien de fois j'ai fait la même chose. Heureux que vous ayez pu résoudre votre problème.
IAmTimCorey

J'ai marqué votre première réponse comme réponse car c'est ce qui m'a amené à trouver la réponse :)
Jim Evans

86

Comme d'autres l'ont déjà dit, l'un de vos types de données de colonnes dans la table source est plus grand que vos colonnes de destination.

Une solution simple consiste simplement à désactiver l'avertissement et à autoriser la troncature. Donc, si vous recevez cette erreur mais que vous êtes sûr qu'il est acceptable que les données de votre ancienne base de données / table soient tronquées (coupées à la taille), vous pouvez simplement faire ce qui suit;

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

Comme ci-dessus, n'oubliez pas de réactiver les avertissements par la suite. J'espère que ça aide.


1
Vous m'avez économisé quelques heures de travail! Prenez tous mes remerciements!
Urasquirrel

De même ici. Parfois, je dois stocker des données dans une table à partir, par exemple, d'un service Web, où le type de données est défini uniquement comme une "chaîne". Je ne peux pas tout faire un Varchar (MAX) ...
Curt

61

Le problème est assez simple: une ou plusieurs colonnes de la requête source contiennent des données qui dépassent la longueur de sa colonne de destination. Une solution simple serait de prendre votre requête source et de l'exécuter Max(Len( source col ))sur chaque colonne. C'est à dire,

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

Comparez ensuite ces longueurs aux longueurs des types de données dans votre table de destination. Au moins un, dépasse sa longueur de colonne de destination.

Si vous êtes absolument certain que cela ne devrait pas être le cas et que vous ne vous souciez pas si ce n'est pas le cas , une autre solution consiste à convertir de force les colonnes de requête source à leur longueur de destination (ce qui tronquera toutes les données trop longues):

Select Cast(TextCol1 As varchar(...))
    , Cast(TextCol2 As varchar(...))
    , Cast(TextCol3 As varchar(...))
    , ...
From ...

Mon processus quotidien a commencé à rompre avec cette erreur. Les données que j'insère étaient toujours assez courtes pour s'adapter et j'avais toujours d'autres lignes (dans le tableau dont je tirais) avec des chaînes surdimensionnées qui n'étaient jamais insérées à cause de mon filtre. Peut-être qu'un index a été reconstruit ou des statistiques ont été mises à jour, mais le fantôme de la machine a décidé un jour qu'il n'aimait plus le plan de requête, car il l'empruntait dans un chemin où les données (trop larges) «pouvaient» être inséré avant qu'il ne soit filtré par le prédicat dans la clause Where. Pour contourner ce problème, j'ai utilisé LEFT () au lieu de CAST - juste moins de caractères à taper.
MikeTeeVee

1
Merci Thomas, c'est étrange, même si je n'ai pas de données trop longues, je dois encore les convertir dans la nouvelle taille de colonne de destination, dès que je l'ai fait, cela a fonctionné.
Michelle

15

SQL Server 2019 renverra enfin un message d'erreur plus significatif.

Les données binaires ou chaîne seraient tronquées => améliorations des messages d'erreur

si vous rencontrez cette erreur (en production), il n'est pas évident de voir de quelle colonne ou ligne provient cette erreur et comment la localiser exactement.

Pour activer un nouveau comportement, vous devez utiliser DBCC TRACEON(460). Nouveau texte d'erreur de sys.messages:

SELECT * FROM sys.messages WHERE message_id = 2628

2628 - Les données de chaîne ou binaires seraient tronquées dans la table '%. * Ls', colonne '%. * Ls'. Valeur tronquée: '%. * Ls'.

Les données de chaîne ou binaires seraient tronquées: remplacement de la tristement célèbre erreur 8152

Ce nouveau message est également rétroporté vers SQL Server 2017 CU12 (et dans une prochaine CU SQL Server 2016 SP2), mais pas par défaut. Vous devez activer l'indicateur de trace 460 pour remplacer l'ID de message 8152 par 2628, au niveau de la session ou du serveur.

Notez que pour l'instant, même dans SQL Server 2019 CTP 2.0, le même indicateur de trace 460 doit être activé. Dans une future version de SQL Server 2019, le message 2628 remplacera le message 8152 par défaut.


SQL Server 2017 CU12 prend également en charge cette fonctionnalité.

Amélioration: remplacement facultatif du message «Les données de chaîne ou binaires seraient tronquées» avec des informations étendues dans SQL Server 2017

Cette mise à jour de SQL Server 2017 introduit un message facultatif qui contient les informations de contexte supplémentaires suivantes.

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

Le nouvel ID de message est 2628. Ce message remplace le message 8152 dans toute sortie d'erreur si l'indicateur de trace 460 est activé.

démo db <> fiddle


ALTER DATABASE SCOPED CONFIGURATION

VERBOSE_TRUNCATION_WARNINGS = {ON | OFF}

S'APPLIQUE À: SQL Server (à partir de SQL Server 2019 (15.x)) et Azure SQL Database

Vous permet d'activer ou de désactiver la nouvelle chaîne ou les données binaires seraient un message d'erreur tronqué. SQL Server 2019 (15.x) introduit un nouveau message d'erreur plus spécifique (2628) pour ce scénario:

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

Lorsqu'il est défini sur ON sous le niveau de compatibilité de base de données 150, les erreurs de troncature déclenchent le nouveau message d'erreur 2628 pour fournir plus de contexte et simplifier le processus de dépannage.

Lorsqu'elle est définie sur OFF sous le niveau de compatibilité de base de données 150, les erreurs de troncature soulèvent le message d'erreur précédent 8152.

Pour le niveau de compatibilité de base de données 140 ou inférieur, le message d'erreur 2628 reste un message d'erreur opt-in qui nécessite l'activation de l'indicateur de trace 460 et cette configuration étendue de base de données n'a aucun effet.


1
Ceci est maintenant également disponible pour SQL Azure: azure.microsoft.com/en-gb/updates/…
Ian Kemp

7

Une autre raison potentielle à cela est si vous avez une valeur par défaut pour une colonne qui dépasse la longueur de la colonne. Il semble que quelqu'un ait touché un gros doigt sur une colonne d'une longueur de 5 mais la valeur par défaut dépassait la longueur de 5. Cela m'a rendu fou car j'essayais de comprendre pourquoi cela ne fonctionnait sur aucun insert, même si tout ce que j'insérais était une seule colonne avec un entier de 1. Parce que la valeur par défaut sur le schéma de table avait cette valeur par défaut violant, il a tout gâché - ce qui nous amène à la leçon apprise - évitez d'avoir des tables avec des valeurs par défaut dans le schéma. :)


1
Je ne pense pas qu'éviter les valeurs par défaut soit une bonne solution. Les valeurs par défaut sont très utiles. Je ne résoudrais pas les "problèmes" de base de données causés par les fautes de frappe en supprimant les valeurs par défaut ...
Jacob H

3

Pour les autres, vérifiez également votre procédure stockée . Dans mon cas, dans ma procédure stockée, CustomSearchj'ai accidentellement déclaré une longueur insuffisante pour ma colonne, donc lorsque j'ai entré un big data, j'ai reçu cette erreur même si j'ai une grande longueur sur ma base de données. Je viens de changer la longueur de ma colonne dans ma recherche personnalisée, l'erreur disparaît. Ceci est juste pour le rappel. Merci.


c'est exactement ce qui m'arrive. les tables source / cible correspondent bien mais le processus stocké avait une #table définie avec une longueur plus courte et cela a échoué là. Je vous remercie!
Joy Walker

3

Cela peut être une erreur difficile. Voici quelques notes tirées de https://connect.microsoft.com/SQLServer/feedback/details/339410/ recherchez le commentaire d'AmirCharania.

J'ai ajusté la réponse donnée par AmirCharania pour les données sélectionnées dans une table réelle, au lieu d'une table temporaire. Sélectionnez d'abord votre ensemble de données dans une table de développement, puis exécutez ce qui suit:

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)

On dirait que MS a fermé le site Connect. Le nouveau lien vers ce numéro est: feedback.azure.com/forums/908035-sql-server/suggestions/… ... toujours marqué comme non planifié. Je pense que le commentaire dont vous parlez a été (ironiquement) tronqué lorsque la migration a eu lieu.
SWalters - Réintégrer Monica

Fait intéressant, le numéro a été de nouveau ouvert sous un titre légèrement différent: feedback.azure.com/forums/908035-sql-server/suggestions/... et il a été répertorié comme "En cours de révision", donc il y a encore de l'espoir.
SWalters - Réintégrer Monica

3

Voici une réponse légèrement différente. Les noms et longueurs de vos colonnes peuvent tous correspondre, mais peut-être que vous spécifiez les colonnes dans le mauvais ordre dans votre instruction SELECT. Disons que tableX et tableY ont des colonnes avec le même nom, mais dans un ordre différent


2

Je suis tombé sur ce problème aujourd'hui, et dans ma recherche d'une réponse à ce message d'erreur informatif minimal, j'ai également trouvé ce lien:

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

Il semble donc que Microsoft n'a pas l'intention d'étendre le message d'erreur de sitôt.

Alors je me suis tourné vers d'autres moyens.

J'ai copié les erreurs pour exceller:

(1 ligne (s) affectée (s))

(1 ligne (s) affectée (s))

(1 ligne (s) affectée (s)) Msg 8152, niveau 16, état 14, ligne 13 La chaîne ou les données binaires seraient tronquées. La déclaration a été terminée.

(1 ligne (s) affectée (s))

a compté le nombre de lignes dans Excel, s'est rapproché du compteur d'enregistrements qui a causé le problème ... a ajusté mon code d'exportation pour imprimer le SQL à proximité ... puis a exécuté les insertions 5 à 10 sql autour du problème sql et réussi à identifier le problème, voir la chaîne qui était trop longue, augmenter la taille de cette colonne, puis le gros fichier d'importation a fonctionné sans problème.

Un peu de hack et une solution de contournement, mais quand vous êtes parti avec très peu de choix, vous faites ce que vous pouvez.


2

Oui, je suis également confronté à ce genre de problème.

REMARKS VARCHAR(500)
to
REMARKS VARCHAR(1000)

Ici, j'ai changé la longueur du fichier REMARQUES de 500 à 1000


2

Je vais ajouter une autre cause possible de cette erreur simplement parce que personne ne l'a mentionnée et cela pourrait aider une future personne (puisque l'OP a trouvé sa réponse). Si la table dans laquelle vous insérez a des déclencheurs, il se peut que le déclencheur génère l'erreur. J'ai vu cela se produire lorsque les définitions de champ de table ont été modifiées, mais pas les tables d'audit.


2

Oui - "une pinte dans un pot d'une demi-pinte n'ira pas". Je n'ai pas eu beaucoup de chance (pour une raison quelconque) avec les différents SP que les gens ont suggérés, MAIS tant que les deux tables sont dans la même base de données (ou vous pouvez les mettre dans la même base de données), vous pouvez utiliser INFORMATION_SCHEMA. COLUMNS pour localiser le (s) champ (s) errant (s), ainsi:

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

Cela vous permettra de faire défiler vers le haut et vers le bas, en comparant les longueurs de champ au fur et à mesure. Les sections commentées vous permettent de voir (une fois non commentées, évidemment) s'il y a des incompatibilités de type de données, ou montrent spécifiquement celles qui diffèrent par la longueur du champ - parce que je suis trop paresseux pour faire défiler - sachez simplement que tout dépend de la source noms de colonne correspondant à ceux de la cible.


J'allais écrire quelque chose comme ça mais vous venez de me faciliter la tâche. très pratique et travaillé comme un charme. J'ai pu l'utiliser pour comparer une table avec plus de 90 colonnes et deux d'entre elles ont sauté immédiatement. Je vous remercie!
Joy Walker

1

J'utilisais une chaîne vide `` lors de la création de la table, puis recevais l'erreur `` Msg 8152, la chaîne ou les données binaires seraient tronquées '' lors de la mise à jour suivante. Cela se produisait en raison de la valeur de mise à jour contenant 6 caractères et plus grande que la définition de colonne prévue. J'ai utilisé "ESPACE" pour contourner ce problème uniquement parce que je savais que je mettrais à jour en masse après la création initiale des données, c'est-à-dire que la colonne n'allait pas rester vide longtemps.

SO BIG CAVEAT ICI: Ce n'est pas une solution particulièrement astucieuse, mais utile dans le cas où vous rassemblez un ensemble de données, par exemple pour des demandes de renseignement ponctuelles où vous créez une table pour l'exploration de données, appliquez un traitement / interprétation en masse et stocker les résultats avant et après pour comparaison / extraction ultérieure. C'est un événement fréquent dans ma ligne de travail.

Vous pouvez initialement remplir en utilisant le mot-clé SPACE ie

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

Les mises à jour ultérieures de "nom_colonne" de 10 caractères ou moins (remplacer le cas échéant) seront alors autorisées sans provoquer d'erreur tronquée. Encore une fois, je n'utiliserais cela que dans des scénarios similaires à celui décrit dans ma mise en garde.


1

J'ai construit une procédure stockée qui analyse une table source ou une requête avec plusieurs caractéristiques par colonne parmi lesquelles la longueur minimale (min_len) et la longueur maximale (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

Je stocke cette procédure dans la base de données master afin de pouvoir l'utiliser dans chaque base de données comme ceci:

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

Et le résultat est:

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,


Remarque: vous ne devez pas utiliser le sp_préfixe pour vos procédures stockées. Microsoft a réservé ce préfixe pour son propre usage (voir Attribution d'un nom aux procédures stockées ) , et vous courez le risque d'un conflit de noms dans le futur. C'est également mauvais pour les performances de vos procédures stockées . Il est préférable d'éviter simplement sp_et d'utiliser quelque chose d'autre comme préfixe - ou pas de préfixe du tout!
marc_s

1

J'ai écrit une procédure de magasin utile pour aider à identifier et résoudre le problème de la troncature du texte (les données de chaîne ou binaires seraient tronquées) lorsque l'instruction INSERT SELECT est utilisée. Il compare les champs CHAR, VARCHAR, NCHAR ET NVARCHAR uniquement et renvoie un champ d'évaluation par champ en cas d'être la cause possible de l'erreur.

EXEC dbo.GetFieldStringTruncate SourceTableName, TargetTableName

Cette procédure stockée est orientée vers le problème de la troncature de texte lorsqu'une instruction INSERT SELECT est effectuée.

Le fonctionnement de cette procédure stockée dépend de l'utilisateur qui a précédemment identifié l'instruction INSERT avec le problème. Insérez ensuite les données source dans une table temporaire globale. L'instruction SELECT INTO est recommandée.

Vous devez utiliser le même nom du champ de la table de destination dans l'alias de chaque champ de l'instruction SELECT.

CODE DE FONCTION:

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

Pour l'instant ne prend en charge que les types de données CHAR, VARCHAR, NCHAR et NVARCHAR . Vous pouvez trouver la dernière version de ce code dans le lien suivant ci-dessous et nous nous entraidons pour l'améliorer. GetFieldStringTruncate.sql

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


1

Si vous êtes sur SQL Server 2016-2017: pour résoudre ce problème, activez l'indicateur de trace 460

DBCC TRACEON(460, 1);
GO

et assurez-vous de l'éteindre après:

DBCC TRACEOFF(460, 1);
GO

la source


0

cela peut également se produire lorsque vous ne disposez pas des autorisations adéquates


2
Vraiment? Une erreur réelle "Chaîne ou données binaires serait tronquée"? Cela semble être une erreur très étrange si vous ne disposez pas des autorisations. Y a-t-il une permission qui vous empêche d'écrire plus qu'une certaine quantité de données ?? (Je suis intéressé parce que je veux vérifier automatiquement la taille du champ lorsque j'obtiens cette erreur - donc si cela peut se produire pour une autre raison, c'est très intéressant!)
Ian Grainger

0

J'ai eu un problème similaire. Je copiais des données d'une table à une table identique dans tout sauf le nom.

Finalement, j'ai vidé la table source dans une table temporaire à l'aide d'une instruction SELECT INTO.

SELECT *
INTO TEMP_TABLE
FROM SOURCE_TABLE;

J'ai comparé le schéma de la table source à la table temporaire. J'ai trouvé que l'une des colonnes était un varchar(4000)quand j'attendais un varchar(250).

MISE À JOUR: Le problème varchar (4000) peut être expliqué ici au cas où vous seriez intéressé:

Pour Nvarchar (Max), je reçois seulement 4000 caractères dans TSQL?

J'espère que cela t'aides.


0

Cette erreur est générée lorsque la colonne d'une table met une contrainte [principalement la longueur]. . Par exemple, si le schéma de base de données pour la colonne myColumn est CHAR (2), alors lorsque votre appel depuis l'une de vos applications pour insérer une valeur, vous devez transmettre une chaîne de longueur deux.

L'erreur le dit essentiellement; La chaîne de longueur trois et plus n'est pas cohérente pour s'adapter à la restriction de longueur spécifiée par le schéma de base de données. C'est pourquoi SQL Server avertit et renvoie une erreur de perte de données / troncature.


0

Veuillez essayer le code suivant:

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]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.