Recebeu um comprimento de coluna inválido do cliente bcp para colid 6

87

Quero fazer o upload em massa dos dados do arquivo csv para o sql server 2005 a partir do código c #, mas estou encontrando o erro a seguir -

Recebeu um comprimento de coluna inválido do cliente bcp para colid 6.

quando a cópia em massa grava no servidor de banco de dados

Sana Sana
fonte

Respostas:

69

Uma das colunas de dados no excel (coluna Id 6) tem um ou mais dados de célula que excedem o comprimento do tipo de dados datacolumn no banco de dados.

Verifique os dados em excel. Verifique também se os dados no Excel estão em conformidade com o esquema da tabela do banco de dados.

Para evitar isso, tente exceder o comprimento dos dados do tipo de dados da string na tabela do banco de dados.

Espero que isto ajude.

Dinesh
fonte
1
Especificamente, se você tiver colunas VARCHAR menores que 4, observe se NULL em seus dados são mal interpretados como a string de 4 caracteres "NULL"
CrazyPyro
195

Sei que esta postagem é antiga, mas encontrei o mesmo problema e finalmente descobri uma solução para determinar qual coluna estava causando o problema e relatar quando necessário. Eu determinei que colidretornado no SqlException não é baseado em zero, então você precisa subtrair 1 dele para obter o valor. Depois disso, ele é usado como o índice da _sortedColumnMappingsArrayList da instância SqlBulkCopy e não o índice dos mapeamentos de coluna que foram adicionados à instância SqlBulkCopy. Uma coisa a notar é que SqlBulkCopy irá parar no primeiro erro recebido, então este pode não ser o único problema, mas pelo menos ajuda a descobri-lo.

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
fonte
4
Isso funciona extremamente bem, obrigado por enviar.
Steven
1
Você sabe se é possível obter também o número da linha?
Gerhard Powell
2
DataFormatException é uma exceção personalizada para que eu pudesse relatar o problema como comprimento de coluna inválido. Ainda não consegui descobrir como obter o número da linha.
b_stil
1
Ótima solução, só falta no catch sqlTran.RollBack ();
pqsk
8
Pode ser óbvio para a maioria, mas "o colid que é retornado no SqlException não é baseado em zero" me ajudou a apagar isso.
panhandel de
4

Enfrentei um tipo de problema semelhante ao passar uma string para a tabela de banco de dados usando a opção SQL BulkCopy. A string que eu estava passando era de 3 caracteres, enquanto o comprimento da coluna de destino era varchar(20). Tentei cortar a string antes de inseri-la no banco de dados usando a Trim()função para verificar se o problema era devido a algum espaço (à esquerda e à direita) na string. Depois de cortar a corda, funcionou bem.

Podes tentar text.Trim()

Liji Chandran
fonte
Isso é ótimo, muito obrigado! Esse foi o motivo exato no meu caso também - espaços em branco à direita e, como resultado, o comprimento da coluna foi excedido.
aleor
Se a string for nula, use o texto? .Trim ()
Charles Plager
1

Verifique o tamanho das colunas na tabela em que você está inserindo / copiando em massa. o varchar ou outras colunas de string podem precisar ser estendidas ou o valor que você está inserindo precisa ser aparado. A ordem das colunas também deve ser a mesma da tabela.

por exemplo, aumentar o tamanho da coluna varchar 30 para 50 =>

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

Nalan Madheswaran
fonte
0

Ótimo código, obrigado por compartilhar!

Acabei usando reflexão para fazer o DataMemberName real retornar a um cliente em um erro (estou usando salvamento em massa em um serviço WCF). Espero que outra pessoa descubra como fiz isso útil.

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
fonte
Como isso pode ser implementado?
Apollo de
0

Recebi esta mensagem de erro com uma versão ssis muito mais recente (vs 2015 enterprise, acho que é ssis 2016). Vou comentar aqui porque esta é a primeira referência que surge quando você google esta mensagem de erro. Eu acho que isso acontece principalmente com colunas de caracteres quando o tamanho do caractere de origem é maior do que o tamanho do caractere de destino. Recebi esta mensagem quando estava usando uma entrada ado.net para ms sql de um banco de dados teradata. Engraçado porque as gravações de oledb anteriores em ms sql tratavam perfeitamente de todas as conversões de caracteres, sem substituições de codificação. O número colid e a coluna de entrada de destino correspondente # que você às vezes recebe com a mensagem colid são inúteis. Não é a coluna quando você faz a contagem regressiva do topo do mapeamento ou algo parecido. Se eu fosse a microsoft, eu ' Você teria vergonha de fornecer uma mensagem de erro que parecesse estar apontando para a coluna do problema, quando não está. Eu achei o problema colid fazendo uma suposição educada e, em seguida, alterando a entrada para o mapeamento para "Ignorar" e, em seguida, execute novamente para ver se a mensagem foi embora. No meu caso e no meu ambiente, corrigi-lo por substr ('ing a entrada Teradata para o tamanho do caractere da declaração ms sql para a coluna de saída. Verifique e certifique-se de que seu substr de entrada se propaga através de todas as conversões de dados e mapeamentos. caso isso não acontecesse e eu tive que deletar todos os meus Data Conversion's e Mappings e começar tudo de novo. Mais uma vez engraçado que OLEDB acabou de lidar com isso e ADO.net lançou o erro e teve que ter toda esta intervenção para fazer funcionar. deve usar OLEDB quando seu destino for MS Sql. s apontando para a coluna do problema quando não está. Eu achei o problema colid fazendo uma suposição educada e, em seguida, alterando a entrada para o mapeamento para "Ignorar" e, em seguida, execute novamente para ver se a mensagem foi embora. No meu caso e no meu ambiente, corrigi-lo por substr ('ing a entrada Teradata para o tamanho do caractere da declaração ms sql para a coluna de saída. Verifique e certifique-se de que seu substr de entrada se propaga através de todas as conversões de dados e mapeamentos. caso isso não acontecesse e eu tive que deletar todos os meus Data Conversion's e Mappings e começar tudo de novo. Mais uma vez engraçado que OLEDB acabou de lidar com isso e ADO.net lançou o erro e teve que ter toda esta intervenção para fazer funcionar. deve usar OLEDB quando seu destino for MS Sql. s apontando para a coluna do problema quando não está. Eu achei o problema colid fazendo uma suposição educada e, em seguida, alterando a entrada para o mapeamento para "Ignorar" e, em seguida, execute novamente para ver se a mensagem foi embora. No meu caso e no meu ambiente, corrigi-lo por substr ('ing a entrada Teradata para o tamanho do caractere da declaração ms sql para a coluna de saída. Verifique e certifique-se de que seu substr de entrada se propaga através de todas as conversões de dados e mapeamentos. caso isso não acontecesse e eu tive que deletar todos os meus Data Conversion's e Mappings e começar tudo de novo. Mais uma vez engraçado que OLEDB acabou de lidar com isso e ADO.net lançou o erro e teve que ter toda esta intervenção para fazer funcionar. deve usar OLEDB quando seu destino for MS Sql. Eu achei o problema colid fazendo uma suposição educada e, em seguida, alterando a entrada para o mapeamento para "Ignorar" e, em seguida, execute novamente para ver se a mensagem foi embora. No meu caso e no meu ambiente, corrigi-lo por substr ('ing a entrada Teradata para o tamanho do caractere da declaração ms sql para a coluna de saída. Verifique e certifique-se de que seu substr de entrada se propaga através de todas as conversões de dados e mapeamentos. caso isso não acontecesse e eu tive que deletar todos os meus Data Conversion's e Mappings e começar tudo de novo. Mais uma vez engraçado que OLEDB acabou de lidar com isso e ADO.net lançou o erro e teve que ter toda essa intervenção para fazer funcionar. deve usar OLEDB quando seu destino for MS Sql. Eu achei o problema colid fazendo uma suposição educada e, em seguida, alterando a entrada para o mapeamento para "Ignorar" e, em seguida, execute novamente para ver se a mensagem foi embora. No meu caso e no meu ambiente, corrigi-lo por substr ('ing a entrada Teradata para o tamanho do caractere da declaração ms sql para a coluna de saída. Verifique e certifique-se de que seu substr de entrada se propaga através de todas as conversões de dados e mapeamentos. caso isso não acontecesse e eu tive que deletar todos os meus Data Conversion's e Mappings e começar tudo de novo. Mais uma vez engraçado que OLEDB acabou de lidar com isso e ADO.net lançou o erro e teve que ter toda esta intervenção para fazer funcionar. deve usar OLEDB quando seu destino for MS Sql. se mapeamentos e comece novamente. Mais uma vez, engraçado que o OLEDB tenha acabado de lidar com isso e o ADO.net lançou o erro e teve que ter toda essa intervenção para fazer funcionar. Em geral, você deve usar OLEDB quando seu destino for MS Sql. se mapeamentos e comece novamente. Mais uma vez, engraçado que o OLEDB tenha acabado de lidar com isso e o ADO.net lançou o erro e teve que ter toda essa intervenção para fazer funcionar. Em geral, você deve usar OLEDB quando seu destino for MS Sql.

homem renascentista
fonte
0

Acabei de descobrir isso e usando o snippet de @b_stil, consegui descobrir a coluna do culpado. E em uma investigação mais aprofundada, percebi que precisava cortar a coluna exatamente como @Liji Chandran sugeriu, mas estava usando IExcelDataReader e não conseguia descobrir uma maneira fácil de validar e cortar cada uma das minhas 160 colunas.

Então me deparei com esta classe, (ValidatingDataReader) classe de CSVReader .

O interessante sobre essa classe é que ela fornece o comprimento dos dados das colunas de origem e destino, a linha culpada e até mesmo o valor da coluna que está causando o erro.

Tudo o que fiz foi apenas cortar todas as colunas (nvarchar, varchar, char e nchar).

Acabei de mudar meu GetValuemétodo para este:

 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;
    }

Espero que isso ajude alguém

Ahmad Tijani
fonte