Problemas con doble insert

09/10/2004 - 05:28 por George | Informe spam
Hola, tengo un problema al insertar en SQL Server,
teniendo como llave foranea, la llave de un registro, que
no he insertado aun, o sea, los 2 registros deben
insertarse al mismo tiempo, relacionados por medio de una
llave.

Tengo una factura, y los datos generales de esa factura
los necesito guardar en la BD, solamente hasta que se
hayan especificado los detalles (Cantidad, Concepto y
Monto) de tal factura, estos detalles pueden ser varios,
el encabezado de la factura, es solo uno, es mas o menos
asi:

*********************Encabezado***********************
Factura No:_________ Mes:____________________

Proveedor:__________ Proyecto:________________

**********************Detalle**********************

CANTIDAD MONTO CONCEPTO
1 $ 100.25 Camisetas
3 $ 250.00 DVD's
12 $ 95.51 Gorras

Esto lo tengo que hacer, tomando en cuenta la
concurrencia de las transacciones (es aplicación web,
pueden haber cientos de personas haciendo lo mismo al
mismo instante)
El encabezado va en una tabla, donde la llave (PK) es
autoincrement, y los detalles llevan otra llave (PK)
autoincrement (además de la llave del encabezado, para
hacer la relacion).
La duda que tengo es, como mantener lo datos del
encabezado temporalmente, hasta darle la opción al
usuario de insertar los detalles, y cuando haya hecho
esto, insertar todo junto, pasando la llave del
encabezado, al detalle, pues al ser autoincrement, me doy
cuenta del valor de la llave hasta que el SQL haga el
insert (o me equivoco?).

Me imagino que no esta muy bien explicado, si logra
entenderme, por favor le agradecería que me ayudara un
poco. He pensado en hacer un DataSet, que mantenga los
datos del encabezado, y cuando el usuario ya haya
declarado los detalles, meterlos en otro dataset, e
insertar todo junto, aunque me queda la duda, de como
pasarle la llave primaria del encabezado, al detalle.

Saludos, y gracias
 

Leer las respuestas

#1 SqlRanger
10/10/2004 - 11:33 | Informe spam
Una solución es como bien dices usar un dataset, que guardarías en una
variable de sesión y que se crearía en el momento en que el usuario metiera
la cabecera. Este Dataset debería tener las dos tablas, cabecera y detalle.
La inserción en la base de datos podría hacerse mediante dos DataAdapter uno
para la tabla cabecera y otro para la detalle. El InsertCommand debería
incluir una instrucción para recuperar el autonumérico. Algo así como:

INSERT INTO Maestro(Campo1, Campo2, ) VALUES ( @Campo1, Campo2, ...)
SELECT SCOPE_IDENTITY() As NombreCampoAutonumérico.

La propiedad UpdatedRowSource del InsertCommand debería ser
FirstReturnedRecord o Both para que el adapter lea el valor devuelto por la
instrucción SQL y actualice el campo en el dataset.

En el dataset debería estar creada una relación entre la tabla cabecera y
detalle, y dicha relacion debería tener establecida la regla de
actualización en cascada, para así, cuando se inserte la cabecera,
automáticamente se establezca la clave externa en los detalles.

Esta solución no es muy escalable, porque consume recursos de memoria en el
servidor. Un dataset por cada usuario que esté insertando facturas. Si no se
espera que haya muchos usuarios haciendo esto, no hay por qué preocuparse,
sin embargo, si hay muchos podría ser fastidioso.

La solución para aumentar la escalabilidad va en detrimento del rendimiento.
Tienes, entre otras, las dos siguientes opciones:
(1) Usar variables de sesión en SQL Server. El web config se puede
modificar para usar esto. Hay primero que ejecutar un script para preparar
SQL Server.
(2) En vez de usar un DataSet en una variable de sesión, ir metiendo la
cabecera y los detalles en dos tablas: CabeceraProvisional y
DetalleProvisional. Guardar en una variable de sesión la clave primaria de
la tabla CabeceraProvisional. y cuando el usuario confirme la operación
pasar los registros a las tablas definitivas. Como puede que el usuario
nunca confirme la operación, habría que limpiar estas tablas provisionales
de forma periódica.


Otra solución que es algo menos costosa (ocupa menos recursos en el servidor
que el dataset) es usar una clase específica para las factuaras:

<Serializable()> _
Public Class Factura

Public NoFactura As Integer
Public Mes As Integer
' Otros campos de la cabecera

Public Detalles As New ArrayList ' aquí metemos los detalles, objetos de
la clase DetalleFactura

Public Sub Añadir()
' Código para insertar la factura en SQL Server
'Iniciar la transacción
' Insertamos la cabecera y obtenemos el autonumérico
' Recorremos los detalles y los vamos insertando, la clave externa
la tenemos ya, obtenida en el paso anterior
' Confirmar la transacción o deshacerla en caso de error
End Sub

End Class

<Serializable()> _
Public Class DetalleFactura
' Campos del detalle Factura
End Class

Tendríamos un objeto de la clase factura en una variable de sesión, que
crearíamos en el momento en que el usuario metiera la cabecera, luego
iríamos añadiendo detalles según el usuario los fuera introduciendo. Cuando
el usuario confirme la operación llamamos al método Añadir.


Otra solución que aumenta el tráfico de red, pero que aumenta la
escalabilidad, es incluir el DataSet o el objeto Factura en el ViewState de
la página en vez de en una variable de sesión.

Saludos:

Jesús López
MVP

Preguntas similares