Ir al contenido


Foto

[RESUELTO] Refrescar relación entre tablas al cambiar de registro en un dbgrid


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

#1 Marcmiralles

Marcmiralles

    Advanced Member

  • Miembros
  • PipPipPip
  • 108 mensajes
  • LocationEspaña

Escrito 28 mayo 2011 - 01:05

Guenas otra vez más.

¿hay algún evento que se produzca en un dbgrid siempre que cambio de fila?

Tengo un dbgrid con el historial de medicación del paciente y lo que quiero es que cuando el usuario cambie de registro, tanto si lo hace directamente con un click ,como si lo hace con las teclas de cursor se refresque la información mostrada en otra zona de la pantalla con dbedits donde se presenta la información técnica del medicamento, que por supuesto está en otra tabla y refresco cambiando el SQL del Query correspondiente.

He probado con el evento keypress, pero solo me hace caso si pulso teclas A-Z no le hacen ni cosquillas las teclas de cursor.

¿Alguien me puede decir que evento o eventos debo usar para controlar que se ha cambiado de fila/registro dentro del grid?

Gracias

Marc Miralles
  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 28 mayo 2011 - 03:20

Podría ser en los equivalentes al evento OnDataChange del TDataSource o el AfterScroll del TDataset, pero esto tiene el inconveniente de que al abrir el dataset asociado con el DBgrid lanza la consulta de detalles cuantas veces como registros haya en la consulta maestra ralentizando la aplicación, lo que yo hago es poner un DBEdit fantasma (width = 0) asociado al campo de la clave principal de la consulta maestra ( la del DBGrid) y capturo el evento OnChange de este DbEdit para lanzar la consulta de detalles, incluso podrías ayudarte de un Timer para activarlo en el evento OnChange del DBEdit, para que sea este quien dispare la consulta de detalles, por si el usuario navega en el dbGrid  muy rápidamente con las flechas (arriba, abajo) del teclado, no lanzar tantas consultas de detalles.

Saludos
  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 28 mayo 2011 - 04:57

Me pregunto, en vos alta, si es posible hacer una relación maestro-detalle entre dos querys.

Saludos

  • 0

#4 Marcmiralles

Marcmiralles

    Advanced Member

  • Miembros
  • PipPipPip
  • 108 mensajes
  • LocationEspaña

Escrito 28 mayo 2011 - 05:05

Me pregunto, en vos alta, si es posible hacer una relación maestro-detalle entre dos querys.

Saludos



Ja,jaja.

Es una forma de decir. Lo hago a manita cambiando la cadena SQL.Text y refrescando ExecSQL.
  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 28 mayo 2011 - 05:16

Hola,

Yo más bién apuntaba a algo como esto:

1) En el Query que hace de maestro poner una consulta como ésta:
SELECT * FROM TABLA_MAESTRO


2) Asignar en la propiedad DataSource del Query que hace de detalle el DataSource del maestro.
3) En el Query detalle poner una consulta con un where que vincule la clave foránea con la primaria a través de un parámetro. Algo como:

SELECT * FROM TABLA_DETALLE WHERE CAMPO_FK :CAMPO_PK


Este parámetro debe llamarse igual al campo del query maestro.

4) Activar el query detalle.

Tendría que probar esta técnica, no se si es lo que buscas o no.

Saludos,
  • 0

#6 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 28 mayo 2011 - 05:47

Me pregunto, en vos alta, si es posible hacer una relación maestro-detalle entre dos querys.

Saludos


Palabras más, palabras menos, es una de las técnicas más usadas en DbExpress con sus Datasets unidireccionales, en donde los parámetros juegan un papel preponderante, máxime si usas la técnica de datasets anidados para  establecer la relación, que obligan a que el nombre del parámetro debe ser igual al nombre del campo responsable de la relación, al punto que el query de detalles de dbExpress pasa al lado del TClientDataset (vía TDatsetProvider del query maestro) apuntando a la propiedad DatasetField de este.

Saludos
  • 0

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 28 mayo 2011 - 07:03

Hola,
Yo mucho de esto de datasets anidados no se...
Al concepto lo había "escuchado" y más o menos así es como lo entendí. Pero nunca lo puse a prueba.

Ahora lo he recreado, en Delphi, pero creo algo no capté bien de aquella ocasión. Yo pruebo hacerlo con IBX, y la base de datos EMPLOYEE de ejemplo que ofrece Firebird. Veamos:

1) Coloqué un TIBDatabase
2) Coloqué un TIBTransaction

3) Configuré la propiedad DefaultTransaction para que tome al TIBTransaction y desde el TIBTransaction me aseguro de que la propiedad DefaultDatabase tenga al TIBDatabase.
4) Armo la conexión, en mi caso remota, y hago una prueba. Todo OK.

5) Coloco dos TIBQuery, uno para el maestro y otro para el detalle.
6) Coloco dos TDataSource, uno para cada uno.
7) Coloco dos TDBGrid.

8) Configuro para el TIBQuery maestro la propiedad Database para que tome el TIBDatabase. Y creo una consulta SQL para extrar los datos de la tabla DEPARMENT.
9) Al DataSource "maestro" le configuro la propiedad DataSet para que tome al Query maestro.
10) Por último, al DBGrid le asocio el Database anterior. Listo, se ven los datos de la tabla maestra.

11) Al Query detalle le configuro la propiedad Database y la correspondiente consulta:

select * from EMPLOYEE where DEPT_NO = :DEPT_NO


Veo en Params que exista el parámetro.

Seguidamente, le asocio en su propiedad DataSource el DataSource del maestro.

12) A continuación me voy a DataSource "detalle" y le asocio en DataSet el Query detalle.
13) Por último al DBGrid detalle le asocio el DataSource detalle.

Listo, eso ya debería ser suficiente como para poder ver como un cambio o un desplazamiento sobre la maestra provoca una consulta SQL que muestre los registros detalles asociados a éste.

No se si es todo lo necesario, pero así es como lo hice y al menos aparentemente funciona. Como he dicho, es la primera vez que hago esta prueba a pesar de haberla oído. Si hay algo más que aclarar o decir les agradecería que lo hicieran notar.

Saludos,
  • 0

#8 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 28 mayo 2011 - 08:14

El procedimiento es correcto Delphius.

Ahora la cuestión es el rendimiento, aunque el procedimiento es correcto, en la práctica presenta serios reparos, imagina una consulta maestra  con una relación maestro - detalles establecida de la manera tradicional (MasterSource) o mediante DatasetAnidado, que devuelva unos 200 registros maestros, al abrirla  recorre los 200 registros y lanza 200 consultas de detalles, este comportamiento en red puede ser grave para el rendimiento.

Una solución posible  es optimizar el procedimiento bien sea haciendo dos consultas maestras independientes, una solo para mostrar en el grid maestro con los campos más relevantes (evitando los blobs, etc),  y una segunda consulta maestra con un parámetro que devuelva un solo registro solo  para las modificaciones,  a esta segunda consulta maestra sí se le puede anidar la consulta de detalles con el método que ya experimentaste, entonces de acuerdo al tipo de aplicación implementar la mejor manera de ejecutar esta segunda.

Y la otra es hacer la relación a mano (como parece que lo está hacendo MarcMiralles), la consulta maestra normal y otra consulta que tome como parámetro un campo de la consulta maestra y una de las maneras de ejecutarla es:

Poner un DBEdit fantasma (width = 0) asociado al campo de la clave principal de la consulta maestra ( la del DBGrid) y capturo el evento OnChange de este DbEdit para lanzar la consulta de detalles, incluso podrías ayudarte de un Timer para activarlo en el evento OnChange del DBEdit, para que sea este quien dispare la consulta de detalles, por si el usuario navega en el dbGrid  muy rápidamente con las flechas (arriba, abajo) del teclado, no lanzar tantas consultas de detalles.


Cuando tengas tiempo puedes probar la forma tradicional contra las dos opciones que te comento, la diferencia en rendimiento es notoria.

Saludos





  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 28 mayo 2011 - 08:34

Hola,
Entiendo que quizá esta opción no sea la mejor opción en cuanto a rendimiento debido a que se requiere lanzar tantas consultas, pero si se las diseña para regresar sólo los registros adecuados y quizá, la de limitar lo más posible la cantidad de registros mediante filtros (uso del where) y mientras el crecimiento de dichas tablas no sea elevado me parece que es una buena opción a considerar.

Disculpa pero no termino de entender tus propuestas, en especial la segunda. Me gustaría que pudieras ahondar en más detalles porque no logro comprender el propósito de ese dbedit fantasma.
En la primera opción que comentas dices que se tiene de dos consultas maestras... ¿La idea es que se destine un Query para mostrar los datos y el otro para relacionarlo de la forma a como la que he propuesto?

Saludos,
  • 0

#10 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 28 mayo 2011 - 11:38

Disculpa pero no termino de entender tus propuestas, en especial la segunda. Me gustaría que pudieras ahondar en más detalles porque no logro comprender el propósito de ese dbedit fantasma.


Todo pasa por el tipo de aplicación en cuestión, pienso igual que tú, que para manejar tablas con pocos datos y en local no hay necesidad de complicarse tanto la vida.

Para el caso del DbEdit fantasma en especial, imagina el siguiente escenario: Un formulario con un DBGrid en la parte izquierda con los nombres y documentos de  identificación de 500 pacientes atendidos en este mes (consulta maestra), en la parte derecha un panel para mirar las historias clínicas (blob), placas radiográficas (blob), fórmulas médicas, laboratorios, etc,  de cada paciente para este mismo periodo (consulta de detalles), las dos consultas tiene en común el campo ID_PACIENTE. Si las relaciono como es tradicional vía MasterSource o vía Dataset anidado, al abir la consulta maestra y tener que traer todos esos campos blob de la consulta de  detalles podría encontrar un serio déficit de rendimiento, entonces opto por hacer dos consultas completamente independientes pasándole como parámetro el ID_PACIENTE a la consulta de detalles, cuando?  Opciones:

OnDataChange del TDataSource: Ineficáz porque al abrir o al desplazarse hace lo mismo que si estuvieran enlazadas las consultas.

OnAfterScroll del TDataset maestro: Presenta la misma situación del anterior.

OnChange del DbEdit Fantasma: Apuntando a ID_PACIENTE de la consulta maestra, primero abro el Dataset maestro y después habilito los controles, de este modo al traer los 500 registros maestros no ha habido necesidad de traer un solo detalle, solo cuando detecta el cambio pasa como parámetro el ID_PACIENTE actual y trae los detalles de ese paciente.

Ahora supongamos que el usuario se desplaza velozmente por el DBGrid maestro con las teclas  (arriba - abajo o con el scroll bar) allí entra en juego un poco más de sofistificación del procedimiento implementando un Timer que se habilite con el evento OnChange del fantasma, espere una fracción de tiempo prudente, lance la consulta y se desactive, de este modo si en lapso del intérvalo del Timer el usuario se desplazó sin detenerse por más de 20 registros, finalmente se habrá lanzado solo una consulta y no 20, mejorando notablemente el rendimiento.

Te reitero que todo depende del tipo de necesidad, espero haber sido claro.

La otra posibilidad de las dos consultas maestras es para otro tipo de escenario, y es más fácil ilustrar con un ejemplo, en cuanto tenga un tiempo elaboro uno y lo posteo.

Saludos




  • 0

#11 Marcmiralles

Marcmiralles

    Advanced Member

  • Miembros
  • PipPipPip
  • 108 mensajes
  • LocationEspaña

Escrito 29 mayo 2011 - 06:29

El procedimiento es correcto Delphius.

Ahora la cuestión es el rendimiento, aunque el procedimiento es correcto, en la práctica presenta serios reparos, imagina una consulta maestra  con una relación maestro - detalles establecida de la manera tradicional (MasterSource) o mediante DatasetAnidado, que devuelva unos 200 registros maestros, al abrirla  recorre los 200 registros y lanza 200 consultas de detalles, este comportamiento en red puede ser grave para el rendimiento.

Una solución posible  es optimizar el procedimiento bien sea haciendo dos consultas maestras independientes, una solo para mostrar en el grid maestro con los campos más relevantes (evitando los blobs, etc),  y una segunda consulta maestra con un parámetro que devuelva un solo registro solo  para las modificaciones,  a esta segunda consulta maestra sí se le puede anidar la consulta de detalles con el método que ya experimentaste, entonces de acuerdo al tipo de aplicación implementar la mejor manera de ejecutar esta segunda.

Y la otra es hacer la relación a mano (como parece que lo está hacendo MarcMiralles), la consulta maestra normal y otra consulta que tome como parámetro un campo de la consulta maestra y una de las maneras de ejecutarla es:

Cuando tengas tiempo puedes probar la forma tradicional contra las dos opciones que te comento, la diferencia en rendimiento es notoria.

Saludos


Mi experiencia de años con los motores de base de datos es que el rendimiento en las relaciones puede ser lento. En las primeras épocas del ADSL en España se me ocurrió montar un programa para cadenas de tiendas en las que la base de datos (mysql) estaba en la central y las tiendas hacían la petición remota, de esa forma tenían todas las delegaciones acceso a los datos del cliente, (deudas, historial, abonos.... etc).

Por aquel entonces usaba Visual FoxPro que llevaba muy bien el tema de las consultas y las relaciones (se establecían como en acces de forma visual). Pues bien, me encontré con un serio problema de rendimiento  al pasar la aplicación de una red local donde el rendimiento era excelente con las consultas  a una ADSL,  donde fue traumatico; la solución fue hacer las relaciones a manita, es decir, crear Consultas SQL cada vez. De esa forma gané velocidad de forma sorprendente y lo que tardaba 10 o más  segundos en refrescar  tablas de más de 100.000 registros con las relaciones se convirtió en décimas de segundo que no eran apreciables, haciendo la petición cada vez lo cual hizo que los clientes estuvieran más que contentos. Es por eso que me acostumbré a hacerlo a manita.

Lo que no sabía era como relacionar en lazarus el movimiento por el DBGrid para poder hacer las consultas cada vez que se cambia de registro y que queda solucionado con el DBEdit fantasma.

:shocked:

Saludos.
  • 0

#12 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 29 mayo 2011 - 10:51

la verdad es que yo lo hago con el evento afterscroll, pero la idea de wilson es muy buena, especialmente por lo del timer, para evitar que en un movimiento rapido por el dbgrid, se generen tantas consultas.

Por ello me pregunto si no puedo disparar el timer en el evento afterscroll en lugar de tener un dbedit fanstama.
  • 0

#13 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 29 mayo 2011 - 11:13

la verdad es que yo lo hago con el evento afterscroll, pero la idea de wilson es muy buena, especialmente por lo del timer, para evitar que en un movimiento rapido por el dbgrid, se generen tantas consultas.

Por ello me pregunto si no puedo disparar el timer en el evento afterscroll en lugar de tener un dbedit fanstama.


El "problema" de dejar el timer  en el  evento OnAfterScroll es la apertura ante todo cuando la consulta devuelve una cantidad considerable de registros, para los movimientos posteriores en el DBgrid no tiene problema, siempre y cuando las consultas sean completamente independientes. Vuelvo a reiterar que todo depende del tipo de aplicación.

Saludos
  • 0

#14 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 29 mayo 2011 - 11:19

que tiempo le das al timer?
  • 0

#15 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 29 mayo 2011 - 11:37

Depende de lo crítico de la consulta y el protocolo de comunicación entre 1000 y 1500 milisegundos.

Saludos
  • 0

#16 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 29 mayo 2011 - 02:05

Hola,

Ahora te entendí. Gracias por la explicación Wilson.  (y)
Y como dices, depende de la aplicación, necesidades, etc.

Yo veo otra posibilidad (aunque no la he probado), o una pseudo-variante: cerrar el DataSource y el DataSet detalle en cuanto no se requiera y habilitarlo en los momentos adecuados. Por ejemplo, como cuando dices de explorar rápidamente por el maestro. Una posibilidad que me imagino es la disponer el timer y hacer que éste abra en cuanto el usuario deja de moverse por el maestro; en otro momento que esté cerrado.
De este modo se podría tener dataset anidados y sólo habilitamos la parte detalle en cuanto sea necesario y no hay que estar empleando "fantasmas". ¿Me explico?

¿Ustedes que opinan?

Saludos,
  • 0

#17 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 29 mayo 2011 - 03:15

Hola,

Ahora te entendí. Gracias por la explicación Wilson. 
Y como dices, depende de la aplicación, necesidades, etc.

Yo veo otra posibilidad (aunque no la he probado), o una pseudo-variante: cerrar el DataSource y el DataSet detalle en cuanto no se requiera y habilitarlo en los momentos adecuados. Por ejemplo, como cuando dices de explorar rápidamente por el maestro. Una posibilidad que me imagino es la disponer el timer y hacer que éste abra en cuanto el usuario deja de moverse por el maestro; en otro momento que esté cerrado.
De este modo se podría tener dataset anidados y sólo habilitamos la parte detalle en cuanto sea necesario y no hay que estar empleando "fantasmas". ¿Me explico?

¿Ustedes que opinan?

Saludos,


Habría que probar, si de pronto  cerrar el DataSource y el Dataset anidado no funcionara (porque Delphi tratará siempre de abrirlo), lo que se podría hacer es dejar sin asignar el DataSource que anida y solo hacerlo cuando necesitemos. Lo del famoso "fantasma" evidentemente es una chapuza, pero son soluciones que surgen en momentos de apuro, solucionan un problema y allí se quedan hasta nueva orden.  :D :D Creo que a todos nos ha pasado.

Saludos
  • 0

#18 Marcmiralles

Marcmiralles

    Advanced Member

  • Miembros
  • PipPipPip
  • 108 mensajes
  • LocationEspaña

Escrito 29 mayo 2011 - 03:32



Habría que probar, si de pronto  cerrar el DataSource y el Dataset anidado no funcionara (porque Delphi tratará siempre de abrirlo), lo que se podría hacer es dejar sin asignar el DataSource que anida y solo hacerlo cuando necesitemos. Lo del famoso "fantasma" evidentemente es una chapuza, pero son soluciones que surgen en momentos de apuro, solucionan un problema y allí se quedan hasta nueva orden.  :D :D Creo que a todos nos ha pasado.

Saludos


Por supuesto, pero por desgracia a veces las chapuzas son lo que funcionan.  Cuando se juntan teórica y práctica y funcionan  es genial, pero a veces  a pesar de que la teórica es muy bonita, la realidad de la práctica es otra y al fin y al cabo el usuario no valorará si hay o no un miembro fantasma, ya que él no lo ve, sin embargo si puede ver que funciona y rápido. Por supuesto si lo podemos hacer bien genial, pero muchas veces los programadores pasamos mucho tiempo con  filigranas informáticas que tienen una solución más fácil, rápida y que funcionará, que al fin y al cabo es lo que nos interesa, ¿o no?

En fin solo son 'pajas' mentales mías jejeje.

Saludos
Marc Miralles



  • 0

#19 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 31 mayo 2011 - 02:01

Hola Marc.

Veo que no has marcado este hilo como resuelto. ¿ Aún te da problemas ?.

Si tienes curiosidad por saber como lo solventamos cada uno, yo utilizo ClientDatasets de Delphi, que ya tienen el mecanismo incorporado para las relaciones maestro-detalle.

Lamentablemente esto no es posible en Lazarus (el ClientDataset es el componente que más echo en falta en mis pruebas sobre Lazarus).

En tu caso, personalmente utilizaría varios Datasets, cargando todos los registros relacionados en los datasets de detalles (a veces también lo hago así, en los casos complejos en que no quiero complicar tanto la estructura interna de relaciones en los clientdatasets).

Para que los datasets de detalles solo muestren los datos relacionados al registro activo en el dataset principal, personalmente prefiero utilizar el evento AfterScroll (para detectar el movimiento de registro), y allí aplicar un filtro para que solo sean visibles los detalles relacionados con ese registro del maestro.

De esta forma no estás consultando continuamente la base de datos (lo cual puede ser un problema, sobretodo en redes lentas). Además de esta forma puedo retrasar la grabación de los datos de detalles, al momento en que quiera grabar todos los datos conjuntos en los datasets maestros y detalles (no hace falta ir guardando los datos en cada movimiento sobre el dataset principal, lo cual te dificulta mucho deshacer los cambios realizados, si en un momento dado el usuario decide que no quiere guardar nada de lo editado en la pantalla).

Es muy sencillo de montar y personalmente este enfoque nunca me ha dado el menor inconveniente, pero si hay algo que no te convence lo podemos comentar.

Salut xiquet.
  • 0

#20 Marcmiralles

Marcmiralles

    Advanced Member

  • Miembros
  • PipPipPip
  • 108 mensajes
  • LocationEspaña

Escrito 31 mayo 2011 - 03:12

Hola Marc.

Veo que no has marcado este hilo como resuelto. ¿ Aún te da problemas ?.

Si tienes curiosidad por saber como lo solventamos cada uno, yo utilizo ClientDatasets de Delphi, que ya tienen el mecanismo incorporado para las relaciones maestro-detalle.

Lamentablemente esto no es posible en Lazarus (el ClientDataset es el componente que más echo en falta en mis pruebas sobre Lazarus).

En tu caso, personalmente utilizaría varios Datasets, cargando todos los registros relacionados en los datasets de detalles (a veces también lo hago así, en los casos complejos en que no quiero complicar tanto la estructura interna de relaciones en los clientdatasets).

Para que los datasets de detalles solo muestren los datos relacionados al registro activo en el dataset principal, personalmente prefiero utilizar el evento AfterScroll (para detectar el movimiento de registro), y allí aplicar un filtro para que solo sean visibles los detalles relacionados con ese registro del maestro.

De esta forma no estás consultando continuamente la base de datos (lo cual puede ser un problema, sobretodo en redes lentas). Además de esta forma puedo retrasar la grabación de los datos de detalles, al momento en que quiera grabar todos los datos conjuntos en los datasets maestros y detalles (no hace falta ir guardando los datos en cada movimiento sobre el dataset principal, lo cual te dificulta mucho deshacer los cambios realizados, si en un momento dado el usuario decide que no quiere guardar nada de lo editado en la pantalla).

Es muy sencillo de montar y personalmente este enfoque nunca me ha dado el menor inconveniente, pero si hay algo que no te convence lo podemos comentar.

Salut xiquet.



Gracias Marc,

Bueno se me fue la olla y no puse el hilo como resuelto |-). Lo que he hecho es poner un miembro fantasma dbEdit y usar el evento change del mismo para hacer el refresco de la tabla hija.

De todos modos lo que me comentas suena práctico e interesante, así que voy a probarlo a ver que tal.

Salut

Marc Miralles
  • 0




IP.Board spam blocked by CleanTalk.