Reçu une longueur de colonne non valide du client bcp pour colid 6

87

Je souhaite télécharger en bloc des données de fichier csv sur le serveur SQL 2005 à partir du code c # mais je rencontre l'erreur ci-dessous -

Reçu une longueur de colonne non valide du client bcp pour colid 6.

lors de l'écriture d'une copie en bloc sur le serveur de base de données

Sana Sana
la source

Réponses:

69

L'une des colonnes de données dans Excel (Id de colonne 6) a une ou plusieurs données de cellule qui dépassent la longueur du type de données de colonne de données dans la base de données.

Vérifiez les données dans Excel. Vérifiez également que les données dans Excel sont conformes au schéma de la table de base de données.

Pour éviter cela, essayez de dépasser la longueur de données du type de données chaîne dans la table de base de données.

J'espère que cela t'aides.

Dinesh
la source
1
Plus précisément, si vous avez des colonnes VARCHAR inférieures à 4, faites attention à ce que NULL dans vos données ne soit mal interprété comme la chaîne de 4 caractères "NULL"
CrazyPyro
195

Je sais que cet article est ancien, mais j'ai rencontré le même problème et j'ai finalement trouvé une solution pour déterminer quelle colonne était à l'origine du problème et le signaler au besoin. J'ai déterminé que le colidretour dans SqlException n'est pas basé sur zéro, vous devez donc en soustraire 1 pour obtenir la valeur. Après cela, il est utilisé comme index de _sortedColumnMappingsArrayList de l'instance SqlBulkCopy et non comme index des mappages de colonnes qui ont été ajoutés à l'instance SqlBulkCopy. Une chose à noter est que SqlBulkCopy s'arrêtera à la première erreur reçue, ce n'est donc peut-être pas le seul problème, mais cela aide au moins à le résoudre.

try
{
    bulkCopy.WriteToServer(importTable);
    sqlTran.Commit();
}    
catch (SqlException ex)
{
    if (ex.Message.Contains("Received an invalid column length from the bcp client for colid"))
    {
        string pattern = @"\d+";
        Match match = Regex.Match(ex.Message.ToString(), pattern);
        var index = Convert.ToInt32(match.Value) -1;

        FieldInfo fi = typeof(SqlBulkCopy).GetField("_sortedColumnMappings", BindingFlags.NonPublic | BindingFlags.Instance);
        var sortedColumns = fi.GetValue(bulkCopy);
        var items = (Object[])sortedColumns.GetType().GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sortedColumns);

        FieldInfo itemdata = items[index].GetType().GetField("_metadata", BindingFlags.NonPublic | BindingFlags.Instance);
        var metadata = itemdata.GetValue(items[index]);

        var column = metadata.GetType().GetField("column", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
        var length = metadata.GetType().GetField("length", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
        throw new DataFormatException(String.Format("Column: {0} contains data with a length greater than: {1}", column, length));
    }

    throw;
}
b_stil
la source
4
Cela fonctionne extrêmement bien, merci de nous avoir soumis.
Steven
1
Savez-vous s'il est possible d'obtenir également le numéro de ligne?
Gerhard Powell
2
DataFormatException est une exception personnalisée afin que je puisse signaler le problème en tant que longueur de colonne non valide. Je n'ai pas encore réussi à comprendre comment obtenir le numéro de ligne.
b_stil
1
Excellente solution, la seule chose qui manque est dans la capture sqlTran.RollBack ();
pqsk
8
Cela peut être évident pour la plupart, mais "le colid qui est retourné dans SqlException n'est pas basé sur zéro" m'a aidé à étouffer cela.
panhandel
4

J'ai rencontré un problème similaire lors de la transmission d'une chaîne à la table Database à l'aide de l'option SQL BulkCopy. La chaîne que je passais était de 3 caractères alors que la longueur de la colonne de destination était varchar(20). J'ai essayé de couper la chaîne avant de l'insérer dans DB en utilisant la Trim()fonction pour vérifier si le problème était dû à un espace (au début et à la fin) dans la chaîne. Après avoir coupé la chaîne, cela a bien fonctionné.

Tu peux essayer text.Trim()

Liji Chandran
la source
C'est super, merci beaucoup! C'était la raison exacte dans mon cas également - un espace blanc de fin et, par conséquent, une longueur de colonne dépassée.
aleor le
Si la chaîne peut être nulle, utilisez le texte? .Trim ()
Charles Plager
1

Vérifiez la taille des colonnes du tableau que vous effectuez une insertion / copie en bloc. le varchar ou d'autres colonnes de chaîne doivent peut-être être étendues ou la valeur que vous insérez doit être ajustée. L'ordre des colonnes doit également être le même que dans le tableau.

Par exemple, augmenter la taille de la colonne varchar 30 à 50 =>

ALTER TABLE [dbo]. [TableName] ALTER COLUMN [ColumnName] Varchar (50)

Nalan Madheswaran
la source
0

Super morceau de code, merci pour le partage!

J'ai fini par utiliser la réflexion pour obtenir le DataMemberName réel à renvoyer à un client en cas d'erreur (j'utilise une sauvegarde en bloc dans un service WCF). J'espère que quelqu'un d'autre trouvera comment je l'ai fait utile.

static string GetDataMemberName(string colName, object t) {
  foreach(PropertyInfo propertyInfo in t.GetType().GetProperties()) {
    if (propertyInfo.CanRead) {
      if (propertyInfo.Name == colName) {
        var attributes = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).FirstOrDefault() as DataMemberAttribute;
        if (attributes != null && !string.IsNullOrEmpty(attributes.Name))
          return attributes.Name;
        return colName;
      }
    }
  }
  return colName;
}

infocyde
la source
Comment cela peut-il être mis en œuvre?
Apollo
0

J'ai reçu ce message d'erreur avec une version ssis beaucoup plus récente (par rapport à 2015 entreprise, je pense que c'est ssis 2016). Je vais commenter ici car c'est la première référence qui apparaît lorsque vous recherchez ce message d'erreur sur Google. Je pense que cela se produit principalement avec les colonnes de caractères lorsque la taille du caractère source est supérieure à la taille du caractère cible. J'ai reçu ce message lorsque j'utilisais une entrée ado.net dans ms sql à partir d'une base de données teradata. C'est drôle parce que le précédent oledb écrit dans ms sql gère parfaitement toute la conversion de caractères sans remplacement de codage. Le numéro de colid et la colonne d'entrée de destination correspondante # que vous obtenez parfois avec le message colid sont sans valeur. Ce n'est pas la colonne lorsque vous comptez à rebours depuis le haut du mappage ou quoi que ce soit du genre. Si j'étais Microsoft, je ' d être gêné de donner un message d'erreur qui semble pointer vers la colonne du problème alors que ce n'est pas le cas. J'ai trouvé le problème colide en faisant une supposition éclairée, puis en changeant l'entrée du mappage sur "Ignorer", puis en réexécutant et voir si le message a disparu. Dans mon cas et dans mon environnement, je l'ai corrigé par substr ('ing l'entrée Teradata à la taille de caractère de la déclaration ms sql pour la colonne de sortie. Vérifiez et assurez-vous que vos substrats d'entrée se propagent à travers toutes vos conversions et mappages de données. Dans mon si ce n'est pas le cas et j'ai dû supprimer toutes mes conversions et mappages de données et recommencer. Encore une fois drôle que OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devrait utiliser OLEDB lorsque votre cible est MS Sql. s pointant vers la colonne du problème quand ce n'est pas le cas. J'ai trouvé le problème colide en faisant une supposition éclairée, puis en changeant l'entrée du mappage sur "Ignorer", puis en réexécutant et voir si le message avait disparu. Dans mon cas et dans mon environnement, je l'ai corrigé par substr ('ing l'entrée Teradata à la taille de caractère de la déclaration ms sql pour la colonne de sortie. Vérifiez et assurez-vous que vos substrats d'entrée se propagent à travers toutes vos conversions et mappages de données. Dans mon si ce n'est pas le cas et j'ai dû supprimer toutes mes conversions et mappages de données et recommencer. Encore une fois drôle que OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devrait utiliser OLEDB lorsque votre cible est MS Sql. s pointant vers la colonne du problème quand ce n'est pas le cas. J'ai trouvé le problème colide en faisant une supposition éclairée, puis en changeant l'entrée du mappage sur "Ignorer", puis en réexécutant et voir si le message a disparu. Dans mon cas et dans mon environnement, je l'ai corrigé par substr ('ing l'entrée Teradata à la taille de caractère de la déclaration ms sql pour la colonne de sortie. Vérifiez et assurez-vous que vos substrats d'entrée se propagent à travers toutes vos conversions et mappages de données. Dans mon si ce n'est pas le cas et j'ai dû supprimer toutes mes conversions et mappages de données et recommencer. Encore une fois drôle que OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devrait utiliser OLEDB lorsque votre cible est MS Sql. J'ai trouvé le problème colide en faisant une supposition éclairée, puis en changeant l'entrée du mappage sur "Ignorer", puis en réexécutant et voir si le message avait disparu. Dans mon cas et dans mon environnement, je l'ai corrigé par substr ('ing l'entrée Teradata à la taille de caractère de la déclaration ms sql pour la colonne de sortie. Vérifiez et assurez-vous que vos substrats d'entrée se propagent à travers toutes vos conversions et mappages de données. Dans mon si ce n'est pas le cas et j'ai dû supprimer toutes mes conversions et mappages de données et recommencer. Encore une fois drôle que OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devrait utiliser OLEDB lorsque votre cible est MS Sql. J'ai trouvé le problème colide en faisant une supposition éclairée, puis en modifiant l'entrée du mappage sur "Ignorer", puis en réexécutant et voir si le message a disparu. Dans mon cas et dans mon environnement, je l'ai corrigé par substr ('ing l'entrée Teradata à la taille de caractère de la déclaration ms sql pour la colonne de sortie. Vérifiez et assurez-vous que vos substrats d'entrée se propagent à travers toutes vos conversions et mappages de données. Dans mon si ce n'est pas le cas et j'ai dû supprimer toutes mes conversions et mappages de données et recommencer. Encore une fois drôle que OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. devrait utiliser OLEDB lorsque votre cible est MS Sql. s et Mappings et recommencer. Encore une fois drôle, OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devez utiliser OLEDB lorsque votre cible est MS Sql. s et Mappings et recommencer. Encore une fois drôle, OLEDB vient de le gérer et ADO.net a jeté l'erreur et a dû avoir toute cette intervention pour que cela fonctionne. En général, vous devez utiliser OLEDB lorsque votre cible est MS Sql.

homme de la Renaissance
la source
0

Je viens de tomber dessus et en utilisant l'extrait de @ b_stil, j'ai pu comprendre la colonne du coupable. Et après une enquête plus approfondie, j'ai pensé que je devais couper la colonne comme @Liji Chandran le suggérait, mais j'utilisais IExcelDataReader et je ne pouvais pas trouver un moyen facile de valider et de couper chacune de mes 160 colonnes.

Ensuite, je suis tombé sur cette classe, la classe (ValidatingDataReader) de CSVReader .

Ce qui est intéressant à propos de cette classe, c'est qu'elle vous donne la longueur des données des colonnes source et de destination, la ligne coupable et même la valeur de la colonne à l'origine de l'erreur.

Tout ce que j'ai fait, c'est simplement couper toutes les colonnes (nvarchar, varchar, char et nchar).

Je viens de changer ma GetValueméthode pour ceci:

 object IDataRecord.GetValue(int i)
    {
        object columnValue = reader.GetValue(i);

        if (i > -1 && i < lookup.Length)
        {
            DataRow columnDef = lookup[i];
            if
            (
                (
                    (string)columnDef["DataTypeName"] == "varchar" ||
                    (string)columnDef["DataTypeName"] == "nvarchar" ||
                    (string)columnDef["DataTypeName"] == "char" ||
                    (string)columnDef["DataTypeName"] == "nchar"
                ) &&
                (
                    columnValue != null &&
                    columnValue != DBNull.Value
                )
            )
            {
                string stringValue = columnValue.ToString().Trim();

                columnValue = stringValue;


                if (stringValue.Length > (int)columnDef["ColumnSize"])
                {
                    string message =
                        "Column value \"" + stringValue.Replace("\"", "\\\"") + "\"" +
                        " with length " + stringValue.Length.ToString("###,##0") +
                        " from source column " + (this as IDataRecord).GetName(i) +
                        " in record " + currentRecord.ToString("###,##0") +
                        " does not fit in destination column " + columnDef["ColumnName"] +
                        " with length " + ((int)columnDef["ColumnSize"]).ToString("###,##0") +
                        " in table " + tableName +
                        " in database " + databaseName +
                        " on server " + serverName + ".";

                    if (ColumnException == null)
                    {
                        throw new Exception(message);
                    }
                    else
                    {
                        ColumnExceptionEventArgs args = new ColumnExceptionEventArgs();

                        args.DataTypeName = (string)columnDef["DataTypeName"];
                        args.DataType = Type.GetType((string)columnDef["DataType"]);
                        args.Value = columnValue;
                        args.SourceIndex = i;
                        args.SourceColumn = reader.GetName(i);
                        args.DestIndex = (int)columnDef["ColumnOrdinal"];
                        args.DestColumn = (string)columnDef["ColumnName"];
                        args.ColumnSize = (int)columnDef["ColumnSize"];
                        args.RecordIndex = currentRecord;
                        args.TableName = tableName;
                        args.DatabaseName = databaseName;
                        args.ServerName = serverName;
                        args.Message = message;

                        ColumnException(args);

                        columnValue = args.Value;
                    }
                }



            }
        }

        return columnValue;
    }

J'espère que cela aide quelqu'un

Ahmad Tijani
la source