Comment puis-je identifier la (les) colonne (s) responsable (s) des «données de chaîne ou binaires seraient tronquées».

31

Je génère automatiquement des requêtes avec le code que j'ai écrit dans SELECT à partir d'une base de données Pg distante et je l'insère dans une base de données SQL Server locale. Cependant, l'un d'eux génère cette erreur:

[Microsoft] [Pilote ODBC SQL Server] [SQL Server] La chaîne ou les données binaires seraient tronquées. (SQL-22001) [l'état était 22001 maintenant 01000]

[Microsoft] [Pilote ODBC SQL Server] [SQL Server] L'instruction est terminée. (SQL-01000) à. \ Insert.pl ligne 106.

Comment savoir quelle colonne génère cette erreur et n'a pas la longueur de l'entrée? Existe-t-il un moyen de le faire sans deviner la force brute varchar?

Evan Carroll
la source

Réponses:

35

Non, il n'est enregistré nulle part. Allez voter et présentez votre analyse de rentabilisation; cela fait partie de la longue liste de choses qui devraient être corrigées dans SQL Server.

Cela a été demandé il y a des années sur Connect (probablement d'abord dans le délai SQL Server 2000 ou 2005), puis à nouveau sur le nouveau système de rétroaction:

Et maintenant, il a été livré dans SQL Server 2019 , SQL Server 2017 CU12 et apparaîtra dans une future SQL Server 2016 SP2 CU.

Dans le tout premier CTP public de SQL Server 2019, il n'apparaît que sous l'indicateur de trace 460. Cela semble un peu secret, mais il a été publié dans ce livre blanc de Microsoft . Ce sera le comportement par défaut (aucun indicateur de trace requis) à l'avenir, bien que vous puissiez le contrôler via une nouvelle configuration de portée de base de données VERBOSE_TRUNCATION_WARNINGS.

Voici un exemple:

USE tempdb;
GO
CREATE TABLE dbo.x(a char(1));

INSERT dbo.x(a) VALUES('foo');
GO

Résultat dans toutes les versions prises en charge avant SQL Server 2019:

Msg 8152, niveau 16, état 30, ligne 5
Les données de chaîne ou binaires seraient tronquées.
La déclaration est terminée.

Maintenant, sur les serveurs CTP SQL Server 2019, avec l'indicateur de trace activé:

DBCC TRACEON(460);
GO

INSERT dbo.x(a) VALUES('foo');
GO
DROP TABLE dbo.x;
DBCC TRACEOFF(460);

Le résultat montre la table, la colonne et la valeur ( tronquée , pas pleine ):

Msg 2628, niveau 16, état 1, ligne 11
Les données de chaîne ou binaires seraient tronquées dans la table «tempdb.dbo.x», colonne «a». Valeur tronquée: 'f'.
La déclaration est terminée.

Jusqu'à ce que vous puissiez tout supprimer et mettre à niveau vers SQL Server 2019, ou passer à Azure SQL Database, vous pouvez modifier votre code "automagique" pour extraire réellement la longueur maximale sys.columns, ainsi que le nom que vous devez y obtenir de toute façon, puis appliquer LEFT(column, max_length)ou quel que soit l'équivalent de PG. Ou, puisque cela signifie simplement que vous perdrez silencieusement des données, essayez de déterminer quelles colonnes sont incompatibles et corrigez les colonnes de destination afin qu'elles tiennent toutes les données de la source. Étant donné l'accès aux métadonnées aux deux systèmes et le fait que vous écrivez déjà une requête qui doit automatiquement correspondre aux colonnes source -> destination (sinon cette erreur ne serait pas votre plus gros problème), vous ne devriez pas avoir à faire de force brute deviner du tout.

Aaron Bertrand
la source
2

Si vous avez accès à l'exécution de l' Assistant Importation et exportation SQL Server à partir de SQL Server Management Studio (cliquez avec le bouton droit sur la base de données> Tâches> Importer des données ...), créez une tâche qui importe à partir de SQL Client en utilisant votre requête comme source de données vers la destination table.

Avant d'exécuter l'importation, vous pouvez consulter le mappage des données et il vous indiquera quelles colonnes ont des types de champ incohérents. Et si vous exécutez la tâche d'importation, il vous indiquera les colonnes qui n'ont pas pu être importées.

Exemple d'avertissement de validation:

Avertissement 0x802092a7: Tâche de flux de données 1: une troncature peut se produire en raison de l'insertion de données de la colonne de flux de données "NARRATIVE" d'une longueur de 316 dans la colonne de base de données "NARRATIVE" d'une longueur de 60. (Assistant d'importation et d'exportation SQL Server)

bubbassauro
la source
1

En fin de compte, je ne pouvais pas trouver un moyen d'obtenir les informations de la colonne sans les écrire moi-même.

Ce message d'erreur a été généré par DBD::ODBC, vous pouvez également l'utiliser sys.columns (max_length)(je ne sais pas comment).

J'ai utilisé du code comme celui-ci sur ma liste de colonnes pour obtenir une liste de tableaux avec deux éléments, le COLUMN_NAMEet MAX_LENGTH(documentés dans DBIcolumn_info() ).

my @max_lengths = map [ @{$_->fetchall_arrayref->[0]}[3,6] ]
    , map $dbh_mssql->column_info('database', 'dbo', $dest_table, $_)
    , @col_mssql
;

J'ai ensuite saisi les exceptions INSERTet imprimé quelque chose d'utile. Dans cet exemple, @$rowles données sont envoyées àsth->execute()

if ($@) {
        warn "$@\n";
        for ( my $idx=0; $idx <= $#{ $row }; $idx++ ) {
                Dumper {
                        maxlength => $max_lengths[$idx]->[1]
                        , name    => $max_lengths[$idx]->[0]
                        , length  => length( $row->[$idx] )
                        , content => $row->[$idx]
                };
        }
        die;
}

Veuillez également voter et voter pour l'autre réponse.

Evan Carroll
la source
2
Je n'ai pas mis de référence de code sys.columnsparce que je n'avais absolument aucune idée du code que vous utilisez actuellement pour générer "automatiquement" vos requêtes. Il n'y a vraiment pas beaucoup plus complexe que je pourrais deviner à incorporer dans votre code SELECT name, object_id, max_length FROM sys.columns;. Puisque vous avez déjà du code automagique qui doit faire cela - ou quelque chose de très similaire - je ne pensais pas qu'un exemple était nécessaire.
Aaron Bertrand
Je ne sais pas comment cela sys.columnsfonctionne avec deux colonnes qui ont le même name. De plus, j'ai fait fonctionner la chose en utilisant la bibliothèque plutôt que sys, pourquoi devrais-je faire cela comme réponse choisie? Microsoft SQL doesn't have x, do y insteadest une contribution valable, mais si vous yêtes inférieur au mien y, je vais faire quelque chose de différent et le marquer comme choisi.
Evan Carroll,
1
Votre question était, essentiellement, comment savoir quelle colonne générait l'erreur (vraisemblablement, afin que vous puissiez corriger ce point, au lieu de réorganiser la solution). Je vous ai dit où chercher: sys.columns. C'est exactement là que vous devriez chercher pour comparer les longueurs de vos colonnes source avec les longueurs des colonnes de destination. La décision vous appartient. Je ne vous ai pas dit comment réparer votre code, car je n'ai absolument aucune idée de la façon dont votre requête automagique était générée en premier lieu, donc, comme je l'ai dit, je ne savais pas comment ajouter les déterminations de longueur à la requête que vous aviez déjà .
Aaron Bertrand
1

Enfin, Microsoft a décidé de fournir des informations utiles pour String or binary would be truncateddémarrer à partir de SQL Server 2016 SP2 CU, SQL Server 2017 CU12 et dans SQL Server 2019.

Les informations incluent désormais à la fois la colonne du tableau incriminé (nom complet) et la valeur incriminée (tronquée à 120 caractères):

Msg 2628, niveau 16, état 1, ligne x chaîne ou les données binaires seraient tronquées dans le tableau «TheDb.TheSchemaTheTable», colonne «TheColumn». Valeur tronquée: '...'. La déclaration est terminée.

Alexei
la source