c# - sqlbulkcopyoptions - SqlBulkCopy no funciona
sqlbulkcopy writetoserver (7)
Tengo un DataSet
poblado de la Hoja de Excel. Quería usar SQLBulk Copy para insertar registros en la tabla LeadId
donde LeadId
es PK.
Tengo un error de seguimiento al ejecutar el siguiente código:
El ColumnMapping dado no coincide con ninguna columna en el origen o destino
string ConStr=ConfigurationManager.ConnectionStrings["ConStr"].ToString();
using (SqlBulkCopy s = new SqlBulkCopy(ConStr,SqlBulkCopyOptions.KeepIdentity))
{
if (MySql.State==ConnectionState.Closed)
{
MySql.Open();
}
s.DestinationTableName = "PCRM_Lead_Hdr";
s.NotifyAfter = 10000;
#region Comment
s.ColumnMappings.Clear();
#region ColumnMapping
s.ColumnMappings.Add("ClientID", "ClientID");
s.ColumnMappings.Add("LeadID", "LeadID");
s.ColumnMappings.Add("Company_Name", "Company_Name");
s.ColumnMappings.Add("Website", "Website");
s.ColumnMappings.Add("EmployeeCount", "EmployeeCount");
s.ColumnMappings.Add("Revenue", "Revenue");
s.ColumnMappings.Add("Address", "Address");
s.ColumnMappings.Add("City", "City");
s.ColumnMappings.Add("State", "State");
s.ColumnMappings.Add("ZipCode", "ZipCode");
s.ColumnMappings.Add("CountryId", "CountryId");
s.ColumnMappings.Add("Phone", "Phone");
s.ColumnMappings.Add("Fax", "Fax");
s.ColumnMappings.Add("TimeZone", "TimeZone");
s.ColumnMappings.Add("SicNo", "SicNo");
s.ColumnMappings.Add("SicDesc", "SicDesc");
s.ColumnMappings.Add("SourceID", "SourceID");
s.ColumnMappings.Add("ResearchAnalysis", "ResearchAnalysis");
s.ColumnMappings.Add("BasketID", "BasketID");
s.ColumnMappings.Add("PipeLineStatusId", "PipeLineStatusId");
s.ColumnMappings.Add("SurveyId", "SurveyId");
s.ColumnMappings.Add("NextCallDate", "NextCallDate");
s.ColumnMappings.Add("CurrentRecStatus", "CurrentRecStatus");
s.ColumnMappings.Add("AssignedUserId", "AssignedUserId");
s.ColumnMappings.Add("AssignedDate", "AssignedDate");
s.ColumnMappings.Add("ToValueAmt", "ToValueAmt");
s.ColumnMappings.Add("Remove", "Remove");
s.ColumnMappings.Add("Release", "Release");
s.ColumnMappings.Add("Insert_Date", "Insert_Date");
s.ColumnMappings.Add("Insert_By", "Insert_By");
s.ColumnMappings.Add("Updated_Date", "Updated_Date");
s.ColumnMappings.Add("Updated_By", "Updated_By");
#endregion
#endregion
s.WriteToServer(sourceTable);
s.Close();
MySql.Close();
}
Bueno, ¿está bien? ¿Los nombres de las columnas existen en ambos lados?
Para ser sincero, nunca me he molestado con las asignaciones. Me gusta mantener las cosas simples: tiendo a tener una tabla de etapas que se parece a la entrada en el servidor, luego SqlBulkCopy
en la tabla de etapas y finalmente ejecuto un procedimiento almacenado para mover la tabla de la tabla de etapas a la tabla actual; ventajas:
- no hay problemas con la corrupción de datos en vivo si la importación falla en cualquier punto
- Puedo poner una transacción solo alrededor del SPROC
- Puedo hacer que el bcp funcione sin iniciar sesión, seguro sabiendo que el SPROC se registrará
- es simple ;-p (sin problemas con las asignaciones)
Como reflexión final: si está tratando con datos masivos, puede obtener un mejor rendimiento utilizando IDataReader
(ya que esta es una API de transmisión, donde, dado que DataTable
es una API almacenada en búfer). Por ejemplo, tiendo a enganchar las importaciones de CSV utilizando CsvReader como fuente para SqlBulkCopy. Alternativamente, he escrito cuñas alrededor de XmlReader
para presentar cada elemento de primer nivel como una fila en un IDataReader
, muy rápido.
Lo que he encontrado es que las columnas en la tabla y las columnas en la entrada deben al menos coincidir. Puede tener más columnas en la tabla y la entrada aún se cargará. Si tienes menos, recibirás el error.
He encontrado el mismo problema al copiar datos de acceso a SQLSERVER 2005 y encontré que las asignaciones de columna son sensibles a las mayúsculas y minúsculas en ambas fuentes de datos, independientemente de la sensibilidad de las bases de datos.
La respuesta de Marc sería mi recomendación (sobre el uso de la tabla de etapas). Esto garantiza que si su fuente no cambia, tendrá menos problemas de importación en el futuro.
Sin embargo, en mi experiencia, puede verificar los siguientes problemas:
Los nombres de columna coinciden en origen y tabla Que los tipos de columna coinciden
Si cree que lo hizo y todavía no tiene éxito. Puedes probar lo siguiente.
1 - Permitir nulos en todas las columnas de su tabla 2 - comentar todas las asignaciones de columna 3 - volver a ejecutar agregando una columna a la vez hasta que encuentre dónde está su problema
Eso debería poner de manifiesto el error
Ha pensado mucho en responder ... Incluso si los nombres de las columnas son iguales, si el tipo de datos difiere, obtendrá el mismo error. Por lo tanto, verifique los nombres de las columnas y su tipo de datos.
PD: las tablas de etapas son definitivamente la forma de importar.
Una de las razones es que: SqlBukCOpy distingue entre mayúsculas y minúsculas. Sigue los pasos:
- En ese caso, primero debe encontrar su columna en la Tabla de origen utilizando el método "Contener" en C #.
- Una vez que su columna de destino coincida con la columna de origen obtenga el índice de esa columna y dé su nombre de columna en SqlBukCOpy.
Por ejemplo: `
//Get Column from Source table
string sourceTableQuery = "Select top 1 * from sourceTable";
DataTable dtSource=SQLHelper.SqlHelper.ExecuteDataset(transaction, CommandType.Text, sourceTableQuery).Tables[0];// i use sql helper for executing query you can use corde sw
for (int i = 0; i < destinationTable.Columns.Count; i++)
{ //check if destination Column Exists in Source table
if (dtSource.Columns.Contains(destinationTable.Columns[i].ToString()))//contain method is not case sensitive
{
int sourceColumnIndex = dtSource.Columns.IndexOf(destinationTable.Columns[i].ToString());//Once column matched get its index
bulkCopy.ColumnMappings.Add(dtSource.Columns[sourceColumnIndex].ToString(), dtSource.Columns[sourceColumnIndex].ToString());//give coluns name of source table rather then destination table so that it would avoid case sensitivity
}
}
bulkCopy.WriteToServer(destinationTable);
bulkCopy.Close();
Me gustaría ir con la idea de puesta en escena, sin embargo, aquí está mi enfoque para manejar la naturaleza sensible a mayúsculas y minúsculas. Feliz de ser criticado en mi linq
using (SqlConnection connection = new SqlConnection(conn_str))
{
connection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = string.Format("[{0}].[{1}].[{2}]", targetDatabase, targetSchema, targetTable);
var targetColumsAvailable = GetSchema(conn_str, targetTable).ToArray();
foreach (var column in dt.Columns)
{
if (targetColumsAvailable.Select(x => x.ToUpper()).Contains(column.ToString().ToUpper()))
{
var tc = targetColumsAvailable.Single(x => String.Equals(x, column.ToString(), StringComparison.CurrentCultureIgnoreCase));
bulkCopy.ColumnMappings.Add(column.ToString(), tc);
}
}
// Write from the source to the destination.
bulkCopy.WriteToServer(dt);
bulkCopy.Close();
}
}
y el método de ayuda
private static IEnumerable<string> GetSchema(string connectionString, string tableName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "sp_Columns";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@table_name", SqlDbType.NVarChar, 384).Value = tableName;
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return (string)reader["column_name"];
}
}
}
}