Inserción masiva de SQL con relaciones padre / hijo, ¿se conserva el orden?

c# sqlbulkcopy sql-server-2008

Pregunta

Similar a estas otras preguntas que se indican a continuación, tengo dos tablas con la estructura:

create table parent (
   recno int identity(1,1) primary key not null,
   groupCode int,
   parentdata varchar(80)
);

create table child (
   parentrecno int not null,
   childdata varchar(80)
)

Necesito insertar unos pocos cientos de miles de registros rápidamente en estas tablas, y las tablas contienen millones de otros registros no relacionados con este inserto y nunca están silenciosos. Debido a la naturaleza padre / hijo, no es un buen candidato (parece) para SqlBulkCopy .

En C # usando SqlCommand con INSERT , obtengo unos 400-500 registros / segundo insertados, y esto es un poco demasiado lento. Pseudocódigo

 foreach(Record r in parentRecords)
 {
      Insert Fields from r into SqlCommand Parameters but not "recno"
      Call ExecuteScalar to insert and fetch the inserted identity value (recno)
      foreach(ChildRecord cr in parentRecords.Children)
      {
          Insert Fields from cr into SqlCommand Parameters
          Insert the identity value (recno) from above into Parameters 
                                                       (as parentrecno)
          Call ExecuteNonQuery to insert the record
      }   
 }

Después de leer esos otros mensajes, se me ocurrió algo. El groupCode adjunto a los registros primarios es exclusivo del conjunto de registros primarios que estoy insertando. ¿Funcionaría para:

  1. A granel inserte los registros primarios con SqlBulkCopy , permitiendo que la inserción genere automáticamente el campo de recno identidad como de costumbre.
  2. Realizar un SELECT únicamente en los registros insertados:

    select recno from parent where groupCode = @thisgroup order by recno;
    
  3. Utilice los valores recuperados para rellenar los campos parentrecno para los registros secundarios en la memoria

  4. Bulk inserte los registros secundarios con SqlBulkCopy

Esto dependería de que los registros primarios ingresen a la tabla SQL en el mismo orden en que se encuentran en el DataTable original (y que los valores de identidad se asignen en ese mismo orden). ¿Es esto algo en lo que puedo confiar?

Preguntas relacionadas:

¿Cómo actualizar las tablas principales y secundarias del conjunto de datos con una clave de identidad generada automáticamente?

SqlBulkCopy y DataTables con relación padre / hijo en la columna de identidad

Respuesta aceptada

Cree dos tablas de preparación con la misma estructura que las tablas de destino, pero no use la identidad en la columna de reconocimiento.

create table parentTmp (
   recno int,
   groupCode int,
   parentdata varchar(80)
);

create table childTmp (
   parentrecno int not null,
   childdata varchar(80)
)

Cargue masivamente sus datos en las tablas de preparación, manteniendo los valores de recno / parentrecno como están.

Luego puede usar la combinación y salida para mover los datos de las tablas de preparación.

-- Table variable to hold mapping between 
-- SourceRecno and TargetRecno
declare @recno table(SourceRecno int, TargetRecno int);

-- Merge data from parentTmp to parent
-- Output old and new recno to @recno
merge parent T
using parentTmp S
on 0=1
when not matched then
  insert (groupCode, parentdata)
    values (S.groupCode, S.parentData)
output S.recno, inserted.recno into @recno;

-- Copy data from childTmp to child
-- Use @recno to get the new recno
insert into child(parentrecno, childdata)
select R.TargetRecno, C.childdata
from childTmp as C
  inner join @recno as R
    on C.parentrecno = R.SourceRecno;

Esto solo funcionará en SQL Server 2008 (y más tarde supongo).


Respuesta popular

Esto no es un inserto masivo absoluto, sino que inserta todos los datos secundarios al mismo tiempo que los datos principales, haciendo solo un viaje de ida y vuelta al DB.

insert into parent(groupcode, parentdata) values(1, 'parent data');
insert into child(parentrecno, childdata) select parentrecno, childdata from (
    select SCOPE_IDENTITY() as parentrecno, 'child data 1' as childdata
    union
    select SCOPE_IDENTITY() as parentrecno, 'child data 2' as childdata
    union
    select SCOPE_IDENTITY() as parentrecno, 'child data 3' as childdata
) childrendata;

Puede crear scripts como este en su código C #, y luego realizar una solicitud por padre.

Tenga en cuenta que este puede no ser un buen enfoque si se sabe que la cantidad de datos secundarios es grande. No conozco los detalles, pero estoy seguro de que el tamaño del script de SQL no puede crecer indefinidamente.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué