propiedades example ejemplos dynamically columns columndefs column bootstrap c# .net sql-server sqlbulkcopy

c# - example - jquery datatable hide column dynamically



SqlBulkCopy y DataTables con relaciĆ³n padre/hijo en la columna de identidad (5)

Tenemos la necesidad de actualizar varias tablas que tienen relaciones padre / hijo basadas en una clave principal de Identidad en la tabla principal, a la que una o más tablas secundarias denominan clave externa.

  • Debido al gran volumen de datos, nos gustaría construir estas tablas en la memoria, luego usar SqlBulkCopy de C # para actualizar la base de datos en masa desde el DataSet o los DataTables individuales.
  • Además, nos gustaría hacer esto en paralelo, desde varios subprocesos, procesos y posiblemente clientes.

Nuestro prototipo en F # es muy prometedor, con un aumento de rendimiento de 34x, pero este código fuerza valores de identidad conocidos en la tabla principal. Cuando no está forzado, la columna Identidad se genera correctamente en la base de datos cuando SqlBulkCopy inserta las filas, pero los valores de Identidad NO se actualizan en el DataTable en memoria. Además, incluso si lo fueran, no está claro si el DataSet arreglaría correctamente las relaciones padre / hijo, por lo que las tablas secundarias podrían escribirse posteriormente con los valores de clave externa correctos.

¿Alguien puede explicar cómo hacer que SqlBulkCopy actualice los valores de Identidad y cómo configurar un DataSet para conservar y actualizar las relaciones padre / hijo, si esto no se hace automáticamente cuando se llama un DataAdapter a FillSchema en los DataTables individuales?

Respuestas que no estoy buscando:

  • Lea la base de datos para encontrar el valor de identidad más alto actual, luego auméntela manualmente al crear cada fila principal. No funciona para múltiples procesos / clientes y, como entiendo, las transacciones fallidas pueden hacer que se omitan algunos valores de Identidad, por lo que este método podría arruinar la relación.
  • Escriba las filas primarias una a la vez y solicite nuevamente el valor de Identidad. Esto derrota al menos algunas de las ganancias obtenidas con el uso de SqlBulkCopy (sí, hay muchas más filas secundarias que las principales, pero todavía hay muchas filas principales).

Similar a la siguiente pregunta sin respuesta:


En primer lugar: SqlBulkCopy no es posible hacer lo que quieres. Como su nombre indica, es solo una "calle de sentido único". Muevo los datos al servidor SQL lo más rápido posible. Es la versión .Net del antiguo comando de copia masiva que importa archivos de texto sin formato en tablas. Por lo tanto, no hay manera de recuperar los valores de identidad si está utilizando SqlBulkCopy.

He hecho un montón de procesamiento de datos a granel y he enfrentado este problema varias veces. La solución depende de su arquitectura y distribución de datos. Aquí hay algunas ideas:

  • Cree un conjunto de tablas de destino para cada subproceso, importe en estas tablas. Al final unir estas tablas. La mayor parte de esto puede implementarse de una manera bastante genérica en la que genera tablas llamadas TABLENAME_THREAD_ID automáticamente a partir de tablas llamadas TABLENAME.

  • Mueve la generación de ID completamente fuera de la base de datos. Por ejemplo, implemente un servicio web central que genere los ID. En ese caso, no debe generar un ID por llamada, sino generar rangos de ID. De lo contrario, la red de sobrecarga se convierte generalmente en un cuello de botella.

  • Intenta generar IDs a partir de tus datos. Si es posible, tu problema habría desaparecido. No digas "no es posible" ayunar. ¿Quizás pueda usar identificadores de cadena que se pueden limpiar en un paso de procesamiento posterior?

Y un comentario más: un aumento del factor 34 cuando se utiliza BulkCopy suena demasiado pequeño. Si desea insertar datos rápidamente, asegúrese de que su base de datos esté configurada correctamente.


La única forma en que podría hacer lo que desea utilizando SqlBulkCopy es insertar primero los datos en una tabla de preparación. Luego use un procedimiento almacenado para distribuir los datos a las tablas de destino. Sí, esto causará una desaceleración pero seguirá siendo rápido.

También puede considerar volver a diseñar sus datos, es decir, dividirlos, desnormalizarlos, etc.



Supongo que lo que se intercambia es el rendimiento de BulkInsert frente a la fiabilidad de la identidad.

¿Puede colocar la base de datos en SingleUserMode temporalmente para realizar su inserción?

Me enfrenté a un problema muy similar con mi proyecto de conversión, en el que agrego una columna de Identidad a tablas muy grandes y tienen hijos. Afortunadamente, pude configurar la identidad de las fuentes principal y secundaria (usé un TextDataReader) para realizar el BulkInsert, y generé los archivos principal y secundario al mismo tiempo.

También obtuve las ganancias de rendimiento de las que estás hablando, la Fuente de OleDBDataReader -> StreamWriter ... y luego TextDataReader -> SQLBulk


set identity_insert <table> on y dbcc checkident son sus amigos aquí. Esto es algo como lo que he hecho en el pasado (ver ejemplo de código). La única advertencia real es que el proceso de actualización es el único que puede insertar datos: todos los demás tienen que salir del grupo mientras se realiza la actualización. Por supuesto, podría hacer este tipo de mapeo programáticamente antes de cargar las tablas de producción. Pero se aplica la misma restricción en las inserciones: el proceso de actualización es el único proceso que se pone en juego.

-- -- start with a source schema -- doesn''t actually need to be SQL tables -- but from the standpoint of demonstration, it makes it easier -- create table source.parent ( id int not null primary key , data varchar(32) not null , ) create table source.child ( id int not null primary key , data varchar(32) not null , parent_id int not null foreign key references source.parent(id) , ) -- -- On the receiving end, you need to create staging tables. -- You''ll notice that while there are primary keys defined, -- there are no foreign key constraints. Depending on the -- cleanliness of your data, you might even get rid of the -- primary key definitions (though you''ll need to add -- some sort of processing to clean the data one way or -- another, obviously). -- -- and, depending context, these could even be temp tables -- create table stage.parent ( id int not null primary key , data varchar(32) not null , ) create table stage.child ( id int not null primary key , data varchar(32) not null , parent_id int not null , ) -- -- and of course, the final destination tables already exist, -- complete with identity properties, etc. -- create table dbo.parent ( id int not null identity(1,1) primary key , data varchar(32) not null , ) create table dbo.child ( id int not null identity(1,1) primary key , data varchar(32) not null , parent_id int not null foreign key references dbo.parent(id) , ) ----------------------------------------------------------------------- -- so, you BCP or otherwise load your staging tables with the new data -- frome the source tables. How this happens is left as an exercise for -- the reader. We''ll just assume that some sort of magic happens to -- make it so. Don''t forget to truncate the staging tables prior to -- loading them with data. ----------------------------------------------------------------------- ------------------------------------------------------------------------- -- Now we get to work to populate the production tables with the new data -- -- First we need a map to let us create the new identity values. ------------------------------------------------------------------------- drop table #parent_map create table #parent_map ( old_id int not null primary key nonclustered , offset int not null identity(1,1) unique clustered , new_id int null , ) create table #child_map ( old_id int not null primary key nonclustered , offset int not null identity(1,1) unique clustered , new_id int null , ) insert #parent_map ( old_id ) select id from stage.parent insert #child_map ( old_id ) select id from stage.child ------------------------------------------------------------------------------- -- now that we''ve got the map, we can blast the data into the production tables ------------------------------------------------------------------------------- -- -- compute the new ID values -- update #parent_map set new_id = offset + ( select max(id) from dbo.parent ) -- -- blast it into the parent table, turning on identity_insert -- set identity_insert dbo.parent on insert dbo.parent (id,data) select id = map.new_id , data = staging.data from stage.parent staging join #parent_map map on map.old_id = staging.id set identity_insert dbo.parent off -- -- reseed the identity properties high water mark -- dbcc checkident dbo.parent , reseed -- -- compute the new ID values -- update #child_map set new_id = offset + ( select max(id) from dbo.child ) -- -- blast it into the child table, turning on identity_insert -- set identity_insert dbo.child on insert dbo.child ( id , data , parent_id ) select id = parent.new_id , data = staging.data , parent_id = parent.new_id from stage.child staging join #child_map map on map.old_id = staging.id join #parent_map parent on parent.old_id = staging.parent_id set identity_insert dbo.child off -- -- reseed the identity properties high water mark -- dbcc checkident dbo.child , reseed ------------------------------------ -- That''s about all there is too it. ------------------------------------