PDA

Ver la Versión Completa : Insercion maestro/detalle para aplicacion cliente servidor


mgaray
14-11-2003, 16:16:27
Hola a todos:
Tengo problemas con la insercion en tablas maestro detalle ya que como tiene que ser una aplicacion cliente servidor cuando dos usuarios entran, YO genero un mismo numero para ambos pero al momento de guardar a un lo deja GUARDAR y al otro le marca un error por clave duplicada, cabe enfatizar que el numero lo genero YO , dicho numero es un consecutivo, que me recomiendan? . La otra es que quiero guardar su detalle al momento de haber guardado el maestro y si hay problemas entonces no guardar. Espero me puedan ayudar, de antemano muchas gracias. :confused:

jachguate
14-11-2003, 16:44:42
En primer lugar te recomiendo que abras dos hilos distintos para dos preguntas distintas, tal como se recomienda en la guia de estilo (http://www.clubdelphi.com/foros/guiaestilo.php).

La solucion natural para evitar el problema de duplicidad de clave es el uso de generadores, secuencias o como lo llame tu base de datos, que es un mecanismo independiente de la transaccion que te asegura un numero diferente para cada registro. Lamentablemente no te puedo orientar mas ya que no indicas en que base de datos trabajas.

Para tu problema de guardar todo o nada (tecnicamente una operacion atomica) la mejor solucion es valerse de una transaccion, y si algo falla en el transcurso de la actualizacion, hacer un rollback, de lo contrario un commit, y listo.

Hasta luego.

;)

mgaray
14-11-2003, 17:03:17
Antes que nada perdon por no respetar las recomendaciones de estilo del foro, pero es que tengo muchos problemas para enviar mis hilos ya que no se porque razon el acceso me es denegado frecuentemente, de cualquier manera te agradezco el comentario.
Pasando a otro asunto, estoy trabajando con sql server 2000, con la generación de un campo autonumerico se resuelve el problema de los usuarios. Tu que dices?. Pero aunado a esto esta mi preocupación de como hacer para que el numero que se asigne en la maestro se lo lleve la tabla detalle, ya que si manejo el caso de un campo autonumerico no sabre que numero le asigno el sql server hasta que se halla guardado y para eso tendria que refrescar la consulta y despues dar de alta los detalles, que me sugieres?.
Gracias por tu ayuda.

Lmas
15-11-2003, 14:00:08
Hola mgaray.

Sin duda alguna, la mejor forma de resolver el problema que planteas de los usuarios es el uso de campos autonuméricos; y en cuanto a los problemas de inserciones y modificaciones en relaciones maestro-detalle...

Uns estructura típica de relación maestro-detalle con una tabla de Facturas y otra de FacturasDetalle:

Facturas
(
id - Autonumérico
ClienteId - Entero largo (Cliente de quien es la factura)
...
resto de campos
);

FacturasDetalle
(
id - Autonumérico
FacturaId - Entero largo (A qué factura de la tabla anterior pertenece este detalle)
...
resto de campos
);


y para mostrar estas tablas tienes dos vistas (o consultas en Access) vwFacturas y vwFacturasDetalle en las que muestras el nombre del cliente, en vez de ClienteId en la primera, y haces algo parecido con la segunda.

En Delphi tendrás dos TADODataset o similar, dsFacturas cuyo origen de datos sea vwFacturas y, dsFacturasDetalle cuyo origen sea vwFacturasDetalle.

Entonces, debes configurar las siguientes propiedades de los Recordset para que al hacer las inserciones en la tabla maestra, automáticamente ADO actualice el campo incremental del registro recién insertado (sólo el nuevo registro, y no toda la tabla), para así poder añadir registros detalle.

En el evento BeforePost de dsFacturas (yo lo pongo en este evento, aunque si no hacer posteriores Requerys o cosas similares, supongo que podría ponerse en el AfterOpen)


dsFacturas.Properties['Unique Table'].Value := 'Facturas';
dsFacturas.Properties['Update Criteria'].Value := adCriteriaUpdCols;
dsFacturas.Properties['Update Resync'].Value := adResyncAll;
dsFacturas.Properties['Resync Command'].Value := 'SELECT * FROM vwFacturas WHERE vwFacturas.id = ?';

y para los detalle:


dsFacturasDetalle.Properties['Unique Table'].Value := 'FacturasDetalle';
dsFacturasDetalle.Properties['Update Criteria'].Value := adCriteriaUpdCols;
dsFacturasDetalle.Properties['Update Resync'].Value := adResyncAll;
dsFacturasDetalle.Properties['Resync Command'].Value := 'SELECT * FROM vwFacturasDetalle WHERE vwFacturasDetalle.id = ?';


Unique Table, indica que sólo se actualice esa tabla de las que forman la vista y ADO no intente actualizar las tablas relacionadas.
Update Criteria, indica el criterio de comparación de las columnas en la actualización (la clave primeria, timestamp, etc... Los posibles valores están en ADOInt.pas)
Update Resync, indica cuando sincronizar la tabla. Los posibles valores también están en ADOInt.
Resync Command, es la instrucción SQL que ADO ejecutará para sincronizar la tabla. En ella ADO sustituye el "?" por la clave primaria de la tabla; en este caso, el campo autoincremental.

Para hacerlo como te indica jachguate en un todo o nada mediante transacciones:

Conexion.BeginTrans;
try
dsFacturas.Append;
tbFacturasClienteId.AsInteger := XXXX;
dsFacturas.Post;
// Primer detalle
dsFacturasDetalle.Append;
tbFacturasDetalleFacturaId.AsInteger := tbFacturasId.AsInteger;
dsFacturasDetalle.Post;
// Segundo detalle
dsFacturasDetalle.Append;
tbFacturasDetalleFacturaId.AsInteger := tbFacturasId.AsInteger;
dsFacturasDetalle.Post;
Conexion.CommitTrans;
except
Conexion.RollbackTrans;
end;


La propiedad LockType de los Datasets debe ser ltOptimistic.

El quid de la cuestión es que ADO automáticamente, tras "dsFacturas.Post", lanza un "SELECT @@identity" que devuelve el valor del último campo incremental que se haya generado en la conexión y tras ese comando lanza el que se haya puesto en la propiedad "Resync Command" del Recordset, sustituyendo el "?" por el valor que acaba de obtener de la consulta anterior. De esta forma en "tbFacturasDetalleFacturaId.AsInteger := tbFacturasId.AsInteger" se está dando al registro detalle la clave primaria con la que está relacionada. Todo este proceso se puede ver con el SQL Profiler del SQL Server.

Lo anterior funciona a partir de SQL Server 2000 y de Access 2000, en versiones anteriores no (no recuerdo exactamente si era necesario también el MDAC 2.5 o superior, lo puedes consultar el el MSDN de Microsoft).

Bueno, vale ya de tanto rollo. ;)

SalU2

SANTIAGUERO
17-11-2003, 01:05:00
Quizas esto te de algo de luz, en SQL existe una fucion o veriable que te devuelve el ultimo numero autogenerado en @@identity

Espeo haber cooperado contigo

mgaray
17-11-2003, 17:20:46
Hola:
Solo para agradecerles su atención y amabilidad al responderme sobre mis problemas, hice lo que me sugirieron y todo resulto a la perfección, muchas gracias por su ayuda.

Saludos

Mgaray.