Se recibió una longitud de columna no válida del cliente bcp para colid 6

87

Quiero cargar datos de archivos csv de forma masiva al servidor SQL 2005 desde el código c #, pero me encuentro con el siguiente error:

Se recibió una longitud de columna no válida del cliente bcp para colid 6.

cuando la copia masiva escribe en el servidor de la base de datos

Sana Sana
fuente

Respuestas:

69

Una de las columnas de datos en Excel (Id. De columna 6) tiene uno o más datos de celda que exceden la longitud del tipo de datos de la columna de datos en la base de datos.

Verifique los datos en Excel. También verifique los datos en Excel para que su formato cumpla con el esquema de la tabla de la base de datos.

Para evitar esto, intente exceder la longitud de los datos del tipo de datos de cadena en la tabla de la base de datos.

Espero que esto ayude.

Dinesh
fuente
1
Específicamente, si tiene columnas que son VARCHAR más pequeñas que 4, tenga cuidado con NULL en sus datos que se malinterpretan como la cadena 4char "NULL"
CrazyPyro
195

Sé que esta publicación es antigua, pero me encontré con el mismo problema y finalmente encontré una solución para determinar qué columna estaba causando el problema e informarlo según sea necesario. Determiné que coliddevuelto en la SqlException no se basa en cero, por lo que debe restar 1 para obtener el valor. Después de eso, se usa como índice de _sortedColumnMappingsArrayList de la instancia de SqlBulkCopy, no como índice de las asignaciones de columnas que se agregaron a la instancia de SqlBulkCopy. Una cosa a tener en cuenta es que SqlBulkCopy se detendrá en el primer error recibido, por lo que puede que este no sea el único problema, pero al menos ayuda a resolverlo.

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
fuente
4
Esto funciona muy bien, gracias por enviarnos.
Steven
1
¿Sabes si es posible obtener también la fila nr?
Gerhard Powell
2
DataFormatException es una excepción personalizada, por lo que podría informar el problema como una longitud de columna no válida. Todavía no he podido averiguar cómo obtener el número de fila.
b_stil
1
Gran solución, lo único que falta es el catch sqlTran.RollBack ();
pqsk
8
Puede ser muy obvio para la mayoría, pero "el colid que se devuelve en la SqlException no está basado en cero" me ayudó a eliminar esto.
Panhandel
4

Enfrenté un tipo de problema similar al pasar una cadena a la tabla de la base de datos usando la opción SQL BulkCopy. La cadena que estaba pasando tenía 3 caracteres, mientras que la longitud de la columna de destino era varchar(20). Intenté recortar la cadena antes de insertarla en la base de datos usando la Trim()función para verificar si el problema se debía a algún espacio (inicial y final) en la cadena. Después de recortar la cuerda, funcionó bien.

Puedes probar text.Trim()

Liji Chandran
fuente
¡Eso es genial, muchas gracias! Esa fue la razón exacta en mi caso también: espacios en blanco finales y, como resultado, se excedió la longitud de la columna.
aleor
Si la cadena puede ser nula, use el texto? .Trim ()
Charles Plager
1

Verifique el tamaño de las columnas en la tabla que está haciendo inserción / copia masiva. Es posible que sea necesario extender el varchar u otras columnas de cadena o el valor que está insertando debe recortarse. El orden de las columnas también debe ser el mismo que en la tabla.

por ejemplo, aumentar el tamaño de la columna varchar 30 a 50 =>

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

Nalan Madheswaran
fuente
0

Excelente código, ¡gracias por compartirlo!

Terminé usando la reflexión para obtener el DataMemberName real para devolverlo a un cliente en caso de error (estoy usando el guardado masivo en un servicio WCF). Con suerte, alguien más encontrará útil cómo lo hice.

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
fuente
¿Cómo se puede implementar esto?
Apollo
0

Recibí este mensaje de error con una versión de ssis mucho más reciente (vs 2015 Enterprise, creo que es ssis 2016). Voy a comentar aquí porque esta es la primera referencia que aparece cuando googleas este mensaje de error. Creo que sucede principalmente con columnas de caracteres cuando el tamaño del carácter de origen es mayor que el tamaño del carácter de destino. Recibí este mensaje cuando estaba usando una entrada de ado.net para ms sql desde una base de datos teradata. Es gracioso porque las escrituras oledb anteriores en ms sql manejaban perfectamente toda la conversión de caracteres sin anulaciones de codificación. El número de colid y el correspondiente número de columna de entrada de destino que a veces se obtiene con el mensaje de colid no tienen valor. No es la columna cuando cuenta hacia atrás desde la parte superior del mapeo ni nada de eso. Si yo fuera Microsoft, yo ' Me avergonzaría dar un mensaje de error que parece que apunta a la columna del problema cuando no lo es. Encontré el problema colido haciendo una suposición fundamentada y luego cambiando la entrada del mapeo a "Ignorar" y luego volví a ejecutar y ver si el mensaje desapareció. En mi caso y en mi entorno, lo arreglé subestrando la entrada de Teradata al tamaño de carácter de la declaración ms sql para la columna de salida. Verifique y asegúrese de que su subestación de entrada se propague a través de todas sus conversiones de datos y asignaciones. En mi En caso de que no fuera así, tuve que eliminar todas mis conversiones de datos y asignaciones y empezar de nuevo. De nuevo, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, usted debe usar OLEDB cuando su objetivo sea MS Sql. s apuntando a la columna del problema cuando no lo es. Encontré el problema colido haciendo una suposición fundamentada y luego cambiando la entrada del mapeo a "Ignorar" y luego volví a ejecutar y ver si el mensaje desapareció. En mi caso y en mi entorno, lo arreglé subestrando la entrada de Teradata al tamaño de carácter de la declaración ms sql para la columna de salida. Verifique y asegúrese de que su subestación de entrada se propague a través de todas sus conversiones de datos y asignaciones. En mi En caso de que no fuera así, tuve que eliminar todas mis conversiones de datos y asignaciones y empezar de nuevo. De nuevo, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, usted debe usar OLEDB cuando su objetivo sea MS Sql. s apuntando a la columna del problema cuando no lo es. Encontré el problema colido haciendo una suposición fundamentada y luego cambiando la entrada del mapeo a "Ignorar" y luego volví a ejecutar y ver si el mensaje desapareció. En mi caso y en mi entorno, lo arreglé subestrando la entrada de Teradata al tamaño de carácter de la declaración ms sql para la columna de salida. Verifique y asegúrese de que su subestación de entrada se propague a través de todas sus conversiones de datos y asignaciones. En mi En caso de que no fuera así, tuve que eliminar todas mis conversiones de datos y asignaciones y empezar de nuevo. De nuevo, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, usted debe usar OLEDB cuando su objetivo sea MS Sql. Encontré el problema colido haciendo una suposición fundamentada y luego cambiando la entrada del mapeo a "Ignorar" y luego volví a ejecutar y ver si el mensaje desapareció. En mi caso y en mi entorno, lo arreglé subestrando la entrada de Teradata al tamaño de carácter de la declaración ms sql para la columna de salida. Verifique y asegúrese de que su subestación de entrada se propague a través de todas sus conversiones de datos y asignaciones. En mi En caso de que no fuera así, tuve que eliminar todas mis conversiones de datos y asignaciones y empezar de nuevo. De nuevo, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, usted debe usar OLEDB cuando su objetivo sea MS Sql. Encontré el problema colido haciendo una suposición fundamentada y luego cambiando la entrada del mapeo a "Ignorar" y luego volví a ejecutar y ver si el mensaje desapareció. En mi caso y en mi entorno, lo arreglé subestrando la entrada de Teradata al tamaño de carácter de la declaración ms sql para la columna de salida. Verifique y asegúrese de que su subestación de entrada se propague a través de todas sus conversiones de datos y asignaciones. En mi En caso de que no fuera así, tuve que eliminar todas mis conversiones de datos y asignaciones y empezar de nuevo. De nuevo, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, usted debe usar OLEDB cuando su objetivo sea MS Sql. sy Mappings y empezar de nuevo. Una vez más, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, debe usar OLEDB cuando su objetivo sea MS Sql. sy Mappings y empezar de nuevo. Una vez más, es gracioso que OLEDB simplemente lo manejó y ADO.net arrojó el error y tuvo que tener toda esta intervención para que funcione. En general, debe usar OLEDB cuando su objetivo sea MS Sql.

hombre del Renacimiento
fuente
0

Me encontré con esto y usando el fragmento de @ b_stil, pude calcular la columna del culpable. Y en una investigación adicional, pensé que necesitaba recortar la columna como sugirió @Liji Chandran, pero estaba usando IExcelDataReader y no pude encontrar una manera fácil de validar y recortar cada una de mis 160 columnas.

Luego me topé con esta clase, (ValidatingDataReader) clase de CSVReader .

Lo interesante de esta clase es que le brinda la longitud de los datos de las columnas de origen y destino, la fila culpable e incluso el valor de la columna que está causando el error.

Todo lo que hice fue recortar todas las columnas (nvarchar, varchar, char y nchar).

Acabo de cambiar mi GetValuemétodo a esto:

 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 esto ayude a alguien

Ahmad Tijani
fuente