Ir al contenido


Foto

[RESUELTO] FIBplus y las transacciones en Firebird


  • Por favor identifícate para responder
16 respuestas en este tema

#1 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 20 noviembre 2010 - 05:39

Buenas,

Llevo ya hechas algunas pruebas y no doy con la solución. Os comento. Mis tablas suelen tener un indice de un sólo campo y numérico. Para asignar el siguiente valor tengo un generador. Hasta ahora asignaba el valor desde Delphi, pero ahora quiero hacerlo desde la propia base de datos mediante un trigger.

Bien, hasta aquí no hay problema. El problema viene cuando desde Delphi quiero saber el código asignado a un nuevo registro para poder hacer una relación maestro/detalle (para asignárselo al detalle). He intentado obtener ese valor de mil y una forma, pero no hay manera, Tengo la transacción en ReadCommited y de la única forma que he logrado saber ese nuevo número es mediante un Commit. Ni refresh, ni CommitRetainig, ni nada, sólo con un Commit.

Sabéis cómo configurar la transacción para poder acceder a ese valor sin tener que hacer un Commit? Ya sabéis que el Commit cierra la transacción y eso hace que pierda la posición del cursor con lo que pierdo el registro y no se cuál es el nuevo.

En fin, gracias de antemano

Nos leemos

  • 0

#2 felipe

felipe

    Advanced Member

  • Administrador
  • 3.283 mensajes
  • LocationColombia

Escrito 20 noviembre 2010 - 06:36

¿Ya probaste algún evento BeforePost en el detalle?


Saludos!
  • 0

#3 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 20 noviembre 2010 - 06:59

Buenas,

Gracias pro contestar, pero el problema es que no se el ID del maestro, así que no puedo crear ningún detalle

Gracias de nuevo

Nos leemos

  • 0

#4 felipe

felipe

    Advanced Member

  • Administrador
  • 3.283 mensajes
  • LocationColombia

Escrito 20 noviembre 2010 - 07:37

Buenas,

Gracias pro contestar, pero el problema es que no se el ID del maestro, así que no puedo crear ningún detalle

Gracias de nuevo

Nos leemos


Pero imagino que tienes creada la relación entre ambas tablas desde Delphi, en ese caso podrías intentar guardarlo en ese evento.



Saludos!
  • 0

#5 jdepaz

jdepaz

    Advanced Member

  • Miembros
  • PipPipPip
  • 264 mensajes
  • LocationMedellín Colombia

Escrito 20 noviembre 2010 - 08:10

Hola buenas noches,

se me ocurre que si el ID siguiente ahora los estas generado desde la base de datos, seria por medio de un SP, entonces creo que tambien el detalle deberia ser grabado mediante un SP, por ejemplo:


1. llamar al SP enviando los encabezados y devuelve el Id obtenido
1.1 el SP genera el siguiente ID
1.2 guarda el encabezado con el ID generado
1.3 devuelve el ID generado
2 llamar al SP enviando los detalles y el Id correspondiente
2.1 guardar el detalle con el ID enviado



Saludos


  • 0

#6 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4.483 mensajes
  • LocationVenezuela

Escrito 20 noviembre 2010 - 09:59

Asi como lo quieres lo veo complicado. La unica forma es cambiando el isolation level a uno que te permita ver transacciones activas pero no es muy recomendado. En este caso del detalle la mejor forma de hacerlo a mi parecer es asignando el valor desde delphi. Otra forma seria haciendo un query al gen_id(migenerador,0) despues de la insercion asi sabes cual fue el siguiente valor pero este metodo no es muy confiable en entorno de redes.

Mi sugerencia sigue tomando el valor desde delphi para la clave principal y utiliza un trigger siempre que se trate del correlativo del documento el cual no debe ir en el detalle

Saludos. Espero te sirva
  • 0

#7 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 21 noviembre 2010 - 09:46

Buenas,

Ante todo gracias a todos por contestar.

jdepaz. como decía, intento asignar el ID en un trigger por lo que la idea del SP no puede ser. Además, hacer la inserción en un SP complicaría bastante la programación, y no es la intención.

eduarcol, como bien dices, lo del gen_id(migenerador,0) no es buena idea porque será un programa que trabajará en red. Lo del nivelo de aislamiento de la transacción, pues es una cosa que no termino de entender, Se supone que todo cambio que yo hago dentro de una misma transacción puedo verlo sin problemas. Es más, se supone que la única diferencia entre un CommitRetaining y un Commit es que el primero conserva el contexto de la transacción. Es por eso que no entiendo por qué no se puede ver el ID hasta la realización de un Commit y no de un CommitRetaining.

Bueno, volveré a cómo lo hacía antes, poner el ID desde Delphi :p

Nos leemos y gracias de nuevo
  • 0

#8 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4.483 mensajes
  • LocationVenezuela

Escrito 21 noviembre 2010 - 10:14

Bueno no se mucho al respecto. Pero me pareciese que el generador no anda en la misma transaction.

Lo digo por lo siguiente. Si tu lees un generador en una transaction y le das rollback se deshacen los cambios pero el generador no vuelve a su valor anterior.
  • 0

#9 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 21 noviembre 2010 - 10:57

Buenas,

Yo no se si van o no en la misma transacción, pero eso mismo ocurre cuando lanzas sentencias SQL de modificación de la estructura de la base de datos (creación/modificación/eliminación de tablas, índices, sp, triggers,......), aunque hagas Rollback, el elemento en cuestión sufre los cambios.

En fin, lo dicho, volveré a la antigua usanza :D

Nos leemos

  • 0

#10 jdepaz

jdepaz

    Advanced Member

  • Miembros
  • PipPipPip
  • 264 mensajes
  • LocationMedellín Colombia

Escrito 21 noviembre 2010 - 11:38

hola,

se me ocurre tener una tabla donde se guarde el ID generado y su correspondiente USUARIO(de la aplicación) quien lo genero, y luego enviar los detalles por medio de un SP y que este SP busque el ID en la tabla por el USUARIO que se le envia y que guarde los detalle.


Saludos

  • 0

#11 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 21 noviembre 2010 - 01:25

Hola Xavi.

Buenas,

Ante todo gracias a todos por contestar.

jdepaz. como decía, intento asignar el ID en un trigger por lo que la idea del SP no puede ser. Además, hacer la inserción en un SP complicaría bastante la programación, y no es la intención.

eduarcol, como bien dices, lo del gen_id(migenerador,0) no es buena idea porque será un programa que trabajará en red. Lo del nivelo de aislamiento de la transacción, pues es una cosa que no termino de entender, Se supone que todo cambio que yo hago dentro de una misma transacción puedo verlo sin problemas. Es más, se supone que la única diferencia entre un CommitRetaining y un Commit es que el primero conserva el contexto de la transacción. Es por eso que no entiendo por qué no se puede ver el ID hasta la realización de un Commit y no de un CommitRetaining.

Bueno, volveré a cómo lo hacía antes, poner el ID desde Delphi :p

Nos leemos y gracias de nuevo


En principio el problema no tiene nada que ver con hacer un Commit o un CommitRetaining.

¿ Como intentas ver el valor generado en el Trigger ?. En el FIBDataset no vas a verlo, puesto que es una información que él no tiene, el valor se asigna en la base de datos, por lo que o bien refrescas ese FIBDataset (y probablemente no vas a poder refrescar el FIBDataset, puesto que como aún no tienes el ID, es incapaz de localizar el registro en el SELECT de refresco), o bien no puedes ver el ID generado, este valor está en la Base de Datos y tienes que ir a recogerlo.

NOTA: Por esto te funciona el Commit, porqué finaliza la transacción y ello te obliga a iniciar una nueva y por tanto a volver a leer la base de datos, con lo que al leer el registro recuperas esa información que solo estaba en la Base de Datos.

Necesitarás un select max(MI_CAMPO_ID) from MI_TABLA, para ver tu último ID asignado. Aunque como dices, en una red te va a dar problemas de poder leer valores erróneos. Tendrías que cambiar la concurrencia al valor por defecto predeterminado en Firebird, el Snapshot, y no el chapucero Read Commited, que es estándar en la mayoría de las bases de datos (siempre me ha parecido alucinante que FibPlus ponga su nivel de concurrencia en las transacciones a Read Commited, cuando el Snapshot es infinitamente mejor, aparte de ser el estándar de Firebird).

Para solucionar todos estos dolores de cabeza, a partir de Firebird 2 se introdujo la cláusula RETURNING en las sentencias INSERT, esto te permite que la misma consulta de inserción devuelva los campos que quieras leer.

Ejplo. : insert into MI_TABLA (...) values (...) returning MI_CAMPO_ID;

Aunque para eso tus componentes tienen que conocer esta característica (con el dbExpress de Interbase, por ejemplo, no puedes lanzar esta consulta, no vas a leer ningún resultado). Y probablemente tendrías que utilizarlo directamente en un FIBQuery, dudo mucho que un FIBDataset con cláusulas returning en su sentencia INSERT sepa tratarlas correctamente (aunque todo es cuestión de probarlo, no siempre nos sorprenden negativamente).

¿ Porqué quieres hacer este cambio ?. Yo genero casi todas mis claves primarias en Delphi, así puedo permitir que el usuario entre los datos de un maestro y un detalle, en memoria dentro de clientdatasets, sin guardar nada en la base de datos hasta el final. (En cambio si generas los ID en la base de datos, tienes que guardar el maestro antes de poder permitir que el usuario entre detalles, ya que en caso contrario no sabrás que ID de relación asignarle. Como mucho en el momento de guardar maestro + detalle, tendrías que guardar primero el maestro, recuperar su ID, recorrer todos los detalles y establecer la clave de relación).

¿ Trabajas con formularios heredados, verdad ?. Yo tengo puesto en el formulario base el código para asignar el ID, así que nunca tengo de preocuparme de nada más que de indicar cual es el generador que corresponde a cada ficha. La verdad es que no le veo ventajas a asignar la clave primaria en triggers o los típicos campos autoincrementales de otras bases de datos (y además, sin esos triggers la base de datos queda más limpia, y es más fácil ver a primera vista los triggers que realmente sí que hacen algo, como mantener el stock de productos, etc. ...).

Saludos.
  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 21 noviembre 2010 - 01:46

Hola, yo me hice bolas al intentar entender el problema  :(

Solamente me permito hacer una mejor aclaración sobre el tema de los generadores: Los generadores escapan al concepto de transacción. Una vez ejecutado un generador no puede volverse atrás, por más que se realice un RollBack.

La única manera de conseguirlo es que se pase un valor -1 en el parámetro Increment de GEN_ID(). Pero esta práctica no es del todo aconsejable. (*)

Una posibilidad, como se ha dicho, de obtener el valor de un generador es lanzar una consulta con algo similar a esto:



sql
  1. GEN_ID(Generador, 0)



De ese modo GEN_ID no incrementará el valor.

(*) En realidad existe otra alternativa, aunque en realidad se trata de resetear o reestablecer el generador a un nuevo valor.

Saludos,
  • 0

#13 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 21 noviembre 2010 - 02:47

Buenas,

Marc, gracias por la aclaración del motivo por el cuál no puedo ver el ID hasta no realizar un Commit.

El motivo de este cambio era más por curiosidad que por funcionalidad. Es una cosa que realmente es de base de datos y por eso quería trasladarlo a ella. Pero visto que da más dolores de cabeza que otra soluciones, la cosa está clara, se traslada nuevamente a Delphi.

Por otro lado, es correcto, Marc, trabajo con herencia visual, por lo que también hacía que mi ficha base se encargara de coger el nuevo ID. Los hijos sólo se encargaban de inicializar una propiedad con el nombre del generador a usar. Y esto será lo que volveré a hacer.

Siempre se aprende algo nuevo, gracias nuevamente a todos ;)

Nos leemos

  • 0

#14 IcebergDelphi

IcebergDelphi

    Advanced Member

  • Moderadores
  • PipPipPip
  • 176 mensajes
  • LocationVillaflores, Chiapas, Mexico

Escrito 21 noviembre 2010 - 07:49

Hola compañero si entedi y estoy en lo correcto lo que necesitas es poner el ID de la tabla maestra a la tabla detalles , para hacer el clasico maestro detalle? si es asi y estas usando FibPlus yo siempre hago lo siguiente:

En las propiedades del TpFIBDataSet llamado: AutoUpdateOptions vas a llenar lo siguiente:

1.-
GeneratorName: GEN_TuTablaMestra_ID ( Esto es en caso de que hayas generado tu trigger y generador mediante el asistente que hay en IbExpert, en caso contrario vas a meter el nombre del generador que creastes manualmente.

2.-
KeyFields: IdTuTablaMaestra (Aca ponemos el Id de tu Tabla)

3.-
UpdateTableName: "TuTabla" (Lo debes de escribir con las comillas Dobles)

4.-
WhenGetGenID: wgOnNewRecord

Con esos pasos , espero no se me haya ido alguno, ya tendras el valor de Tu campo ID de tu tabla maestra y ya teniendo el valor podras asignarle el ID de tu tabla maestra a tu tabla detalles, saludos espero haberte ayudado.

:cool:




  • 0

#15 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 21 noviembre 2010 - 10:53

Buenas,

Como dice Marc, a veces los componentes nos sorprenden gratamente y éste es uno de ellos. Gracias IcebergDelphi, efectivamente se hace así. Ahora ya puedo separar claramente la gestión de base de datos de la gestión Delphi que es lo que buscaba (si es que hay que conocer los componentes!!! jejejejeje) :)

Nos leemos
  • 0

#16 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 21 noviembre 2010 - 11:57

Vaya, como se requiere de meter mano a los componentes, ahí están y uno haciendo "castillos en el aire" :D :D :D

Salud OS
  • 0

#17 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 22 noviembre 2010 - 06:21

Saludos.

Cabe añadir que los FIBPlus, utilizan en las relaciones maestro/detalle la clausula MAS.

Con dicha clausula ellos mismos se encargan de pasar el valor del PK de la tabla Maestro a la tabla Detalle.Me explico:


sql
  1. TABLA MAESTRO(ID, DESCRIPCION);
  2. TABLA DETALLE(ID_MAESTRO, CAMPO1, CAMPO2);



Obviamente tenemos el Trigger y el Generador.  La sentencia del Insert en el componente DataSet de la tabla Detalle sería:

sql
  1. INSERT INTO DETALLE (ID_MAESTRO, CAMPO1, CAMPO2)
  2. VALUES(:MAS_ID_MAESTRO, :CAMPO1, :CAMPO2)



Con lo anterior más la configuración presentada por IcebergDelphi es suficiente para hacer funcionar la relación maestro/detalle.
  • 0




IP.Board spam blocked by CleanTalk.