PDA

Ver la Versión Completa : Como guardar Maestro Detalles entorno de red


DamianG
05-10-2010, 17:01:10
Hola mi consulta es la siguiente: tengo una tabla maestro (t1) y otra detalles (t2), como seria la forma correcta de guardar la informacion?

Ejemplo 1
---------
insert into t1 (c1, c2) values .....
try
commit
except end

insert into t2 (c1, c2, c3) values .....
try
commit
except end

o

Ejemplo 2
---------

try

insert into t1 (c1, c2) values .....
insert into t2 (c1, c2, c3) values .....
commit

except end

En un prinicipio lo hacia como en el ejemplo 2, pasa que a veces me daba error de transacciones pendientes en un entorno de red... lo cambie por el ejemplo 2 y todo bien, pero no estoy seguro de estar haciendo las cosas correctamente.

Gracias

guillotmarc
05-10-2010, 17:33:56
La forma correcta de hacerlo es la 2), así si hay algún problema se cancela la transacción con lo que no se guarda ningún registro, ni en el maestro ni en el detalle (En cambio con la opción 1) nos podemos encontrar facilmente con que hemos guardado el maestro, pero no hemos podido guardar los detalles, con lo que tenemos información incompleta e incoherente en la base de datos).

En todo caso la opción 1) tampoco te debería dar esos problemas de transacciones pendientes. Si quieres buscar la causa deberías darnos más información : mensaje de error exacto, componentes usados, código Delphi real, etc. ...

DamianG
05-10-2010, 21:15:05
Los componentes utilizados son los de Interbase, para las tablas utilizo tibquery.

El error que sale cada tanto es: (estando en red)

lock conflict on no wait transaction
deadlock
update conflicts with concurrent update
concurrent transaction number is 83114.

El código:

tengo las tablas FACVTA (FACTURAS), DETFAC (DETALLES DE PRODUCTOS VENDIDOS, ARTICULOS (PARA ACTUALIZAR LAS CANTIDADES)

---- guardo encabezado
insert into factura_encabezado (...) values ...
execsql;

---- guardo detalles de facturas y actualizo cantidades

Recorro una tabla temporal y voy insertando en los datalles los productos vendidos y actualizando cantidades

first
while ... not eof do
begin

insert into factura_detalles (...) values ....
execsql;

//aca mismo actualizo las cantidad de la tabla articulos

update articulos set cantid = cantid + .... where codart = facturas_detalles.codart
execsql;

next
end //while

tablascommit; (Aqui surge el error cada tanto)

//PROCDIMIENTO TABLASCOMMIT QUE USO
procedure tablascommit;
begin
try
datafb.IBTransaction1.CommitRetaining;
except
datafb.IBTransaction1.RollbackRetaining;
end;
end;

sino se entiende pongo el codigo real, no lo puse por lo extenso, gracias!

guillotmarc
05-10-2010, 22:14:17
lock conflict on no wait transaction
deadlock
update conflicts with concurrent update
concurrent transaction number is 83114.


Este error es un deadlock, un bloqueo, no es un problema de tener transacciones pendientes, es un problema de que dos transacciones quieren modificar el mismo registro (pueden ser en la misma máquina, y por lo tanto tu programa mantiene dos transacciones abiertas que modifican las mismas tablas, o bien puede ser que en dos máquinas distintas intentan modificar el mismo registro).

NOTA: Los triggers también cuentan. De manera que si al añadir productos vendidos, vas actualizando su stock. Entonces dos maquinas distintas, aunque den de alta ventas distintas, van a intentar modificar el stock del mismo producto, y vas a tener un bloqueo en ese registro.

Saludos.

rastafarey
07-10-2010, 05:06:23
Puedes crear una vista actualizable que trbajo sobre ambas tablas y de esa manera pueder verificar el error de bloque. Aunque es muy raro que firebird te de error de bloque ya usa versionado de registros. Debes estar cometiendo un error y no lo estas notando. Has la prueba con una vista actualizable.

guillotmarc
07-10-2010, 10:30:24
Puedes crear una vista actualizable que trbajo sobre ambas tablas y de esa manera pueder verificar el error de bloque. Aunque es muy raro que firebird te de error de bloque ya usa versionado de registros. Debes estar cometiendo un error y no lo estas notando. Has la prueba con una vista actualizable.

En realidad no es nada raro.

Coge un registro, modifícalo con un UPDATE pero no termines la transacción.

Ahora en otro equipo intenta modificar el mismo registro. No vas a poder hasta que el primer equipo no finalice esa transacción. (Por eso me gusta trabajar con ClientDatasets, nunca se dejan transacciones abiertas, y cuando se hacen cambios en la b.b.d.d. las transacciones duran el mínimo tiempo posible. Cuanto más cortas sean tus transacciones menos posibilidades tendrás de encontrarte bloqueos).

NOTA: El sistema de versiones son para las lecturas.

DamianG
07-10-2010, 13:37:18
Como se hace haria en este caso

mis pasos son
guardo encabezado
guardo detalles
(al recorrer los detalles que tengo en una tabla temporal voy actualizando la tabla real de productos las cantidades, descuento de stock)

y luego hago un commitretaining

se puede dar que en otra pc justo esten realizando ventas de otro punto de venta y tambien pueden haber vendido un mismo producto que el anterior, el error en cuestion dejo de darlo al poner un commit dentro del bucle al actualizar el stock de los productos.

Otra vez muchas gracias!

guillotmarc
07-10-2010, 14:36:39
Hola.

La forma en que te recomiendo hacerlo es :

Abrir Transacción
Guardar Encabezado
Guardar Detalles
Finalizar Transacción

Como decía en el mensaje anterior, las transacciones tienen que ser lo más cortas posibles. Tienes que diferenciar entre transacciones que solo consultan datos y transacciones que los modifican. Las transacciones que consultan datos pueden permanecer abiertas durante minutos, ya que no bloquean nada, pero las transacciones que modifican datos solo deberían existir durante unos milisegundos : se abre, modificas y cierras la transacción, puesto que los datos que modifiques van a estar bloqueados y no podrán ser modificados (NOTA: por eso tu problema mejora al poner COMMITS dentro del bucle, en ese momento estás liberando el bloqueo sobre el producto introducido).

Asi pues, si tienes transacciones para mostrar datos, las puedes dejar abiertas tanto tiempo como necesites. Pero si quieres modificar datos, tienes que abrir una nueva transacción, modificar los datos, y cerrarla en el menor tiempo posible.

NOTA: Esta es una de las razones que hacen tan recomendables los ClientDatasets : nunca dejan transacciones abiertas, cuando abres una tabla, abre una transacción, lee los datos y los copia en memoria local, y cierra la transacción. A partir de ese momento trabajas en la copia local que tienes de los datos (sin que exista ningún tipo de transacción abierta). En el momento en que guardas los cambios del ClientDataset, se abre una transacción, aplica los INSERTS o UPDATES para transmitir los cambios a la base de datos, e inmmediatemente cierra la transacción. Las probabilidades de encontrarte con deadlocks son ínfimas (y en caso de que ocurra, solo tienes que volver a guardar, ya que la transacción que te bloqueó inicialmente ya ha terminado seguro).

Toni
08-10-2010, 00:33:34
Hola compañeros,

Una pregunta Guillotmarc, entonces en una aplicacion grande en la que hay muchas consultas de datos (solo lectura) y entradas de datos varias, tu crees que seria necesario o aconsejable usar varios componentes de transacciones?

Es decir, tener un componente de transaccion para los clientdataset de solo lectura y otro componente de transaccion para los clientdataset de entrada modificacion de datos?

A todo esto, utilizo firebird+ibx+clientdataset.

Tal y como esta actualmente, todo con un solo componente TIBTransaction realmente a mi no me genera problemas, pero si realmente se puede mejorar o minimizar problemas me interesaria saberlo.



Saludos,

guillotmarc
08-10-2010, 11:25:52
Hola compañeros,

Una pregunta Guillotmarc, entonces en una aplicacion grande en la que hay muchas consultas de datos (solo lectura) y entradas de datos varias, tu crees que seria necesario o aconsejable usar varios componentes de transacciones?

Es decir, tener un componente de transaccion para los clientdataset de solo lectura y otro componente de transaccion para los clientdataset de entrada modificacion de datos?

A todo esto, utilizo firebird+ibx+clientdataset.

Tal y como esta actualmente, todo con un solo componente TIBTransaction realmente a mi no me genera problemas, pero si realmente se puede mejorar o minimizar problemas me interesaria saberlo.



Saludos,

Hola Toni.

Si siempre utilizas componentes IBX+ClientDataset no hay ninguna necesidad de tener más de una transacción. Puesto que el ClientDataset nunca deja abierta la transacción. Solo la abre momentáneamente en el momento de cargar datos o en el momento de guardarlos, y la cierra inmediatamente. Nunca permanece abierta más de unos milisegundos.

Otra cosa es que utilizaras también componentes IBDataSet. Estos componentes requieren que la transacción esté abierta tanto tiempo como el Dataset permaneza abierto. En ese caso, mientras tengamos transacciones abiertas, sí que deberíamos tener diferentes transacciones, y ser muy cuidadosos de cuando se abren, se confirman, en que transacción se hacen los cambios, etc. ...

Toni
08-10-2010, 12:12:25
Muchas gracias por la respuesta Guillotmarc,

El tema de las transacciones como te comentaba realmente no me daba, era por estudiar si era conveniente reestructurar el proyecto. Pero ya me queda claro que no.

Lo que si me que da algun que otro quebradero de cabeza utilizando 'firebird+ibx+clientdataset' es al actualizar o modifcar algunas tablas, que da el mensaje este de que no encuentra el registro o otro usuario lo ha modificado. El problema es que no encuentra el registro sobre todo en las tablas que tienen un llave primaria de varios campos. (no pasa siempre, incluso rara vez)

Lo he podido solucionar 'temporalmente' en el caso de estas tablas creando los campos estaticos y definiendo que campos son los que forman parte de la llave primaria. Con esto se soluciona, pero me gustaria encontrar una solucion que me permita seguir utiizando los campos de forma 'dinamica', es decir que no esten estaticos en la aplicacion.

guillotmarc
08-10-2010, 14:16:56
Sí. Ese es un problema del ClientDataset, que debes tener los campos estáticos y debes definir cuales forman la clave primaria.

Yo como siempre creo los campos estáticos, no tengo problemas con ello.

En tu caso, quizás después de abrir el ClientDataset, puedes especificar por código quienes son los campos de clave primaria.

Es decir.

ClientDataset1.Open.
IBDataset1.FieldByName('CAMPO').ProviderFlags := IBDataset1.FieldByName('CAMPO').ProviderFlags + pfInKey;

Pero no sé si aun estarán disponibles los campos en el IBDataset, para poder marcarlos como los campos de clave primaria.

Saludos.

rastafarey
11-10-2010, 04:51:27
Tenias razon. De verdad tenia mucho timepo sin ver este error. Como nunca dejo transaciones pendientes. Tenia muchismo tiempo sin ver este error de bloqueo. Ya ni me acordaba que me dio mil dolores de cabezas en mis inicios de programdor. Normalmente no le dejo la tarea de actualizacion de datos a los db aware sera por eso que no se me han presentados los bloqueos.