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
c#
.net
sql-server
database
sql-server-2005
Sana Sana
fuente
fuente
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
colid
devuelto 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_sortedColumnMappings
ArrayList 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; }
fuente
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 laTrim()
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()
fuente
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)
fuente
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; }
fuente
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.
fuente
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
GetValue
mé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
fuente