Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Refrescar registros de grid automaticamente de la BD sin intervención del usuario (https://www.clubdelphi.com/foros/showthread.php?t=78222)

carlo_acp 30-03-2012 17:11:19

Refrescar registros de grid automaticamente de la BD sin intervención del usuario
 
Saludos amigos
Estoy trabajando en una aplicación en la que existe 1 ventana en donde se crean registros ubicada en un terminal. Existe otra ventana, que está en otro terminal, en donde se tiene que visualizar estos registros creados, la idea es que tiene que actualizarse sin intervención del usuario y que sea al momento que se crea el registro en el otro terminal.

Estuve buscando y probando de muchas maneras como hacer esto posible, pero hasta ahora solo tengo una opción que es hacer un timer y que cada "n" segundos haga un refresh del ClientDataSet y me actualice el grid, pero debería esperar el tiempo y en algunas ocasiones es mucho tiempo. Que opinan, como se puede hacer esto, gracias por sus opiniones.

ElDioni 30-03-2012 18:27:22

Hola,

Al timer le puedes poner el intervalo que quieras, no se porque dices que es mucho tiempo, será mucho si le programas mucho, vamos, digo yo.

Saludos.

carlo_acp 30-03-2012 19:47:09

Hola gracias por tu respuesta, bueno te explico mas, se trata de un negocio en el cual el tiempo es muy importante, pues los nuevos registros de pedidos deben llegar al instante, tendría que poner un refresh en el timer de 10 seg o 5 seg y ya te imaginas como estaría esa consulta via red, pues habrá tiempos en los cuales no habrá mucho movimiento de pedidos.

Neftali [Germán.Estévez] 30-03-2012 20:17:48

Creo que otras veces hemos comentado algo similar.
Una opción similar a la que te han comentado, pero más eficiente, es no refrescar la tabla para ver si hay pedidos nuevos (que puede ser una consulta costosa) sino utilizar una pequeña tabla (por ejemplo 1 campo y 1 registro) que te sirva de aviso para saber si tienes que refrescar la tabla real.

De esta forma las consultas casi no generan tráfico por la red (o generan mucho menos) y además no afectan a ninguna tabla importante.

Cuando exista un nuevo pedido, modificas esta tabla de aviso de forma simultánea, y al hacer la consulta sobre esta tabla y detectar que hay nuevo pedido, entonces actualizas la tabla de pedidos.

Otras opciones más complejas, podrían ser utilizar un sistema paralelo, por ejemplo con sockets para no interferir en la Base de Datos, para mandar los avisos.

carlo_acp 30-03-2012 22:05:11

Gracias Neftali, entonces el timer haria consultas a esa tabla si hubo modificaciones y si las hubo, hace la consulta a la tabla con los registros actualizados para actualizar su grid. lo entiendo asi?

Chris 30-03-2012 22:32:08

Cita:

Empezado por carlo_acp (Mensaje 428958)
Gracias Neftali, entonces el timer haria consultas a esa tabla si hubo modificaciones y si las hubo, hace la consulta a la tabla con los registros actualizados para actualizar su grid. lo entiendo asi?

No, no se trata de utilizar una tabla adicional. Se trata de utilizar la misma tabla de pedidos que ya tienes. Por ejemplo:
Código SQL [-]
Select count(p.id) from pedidos p where p.tomado_fecha > :ultima_fecha_vista

Si la consulta anterior te trae como resultado más de 0 -cero-, entonces refrescas por algún medio no intrusivo la tabla principal.

carlo_acp 30-03-2012 23:00:13

Gracias Cris, haciendo comparaciones de operaciones y tiempos, corrígeme si me equivoco de estas conclusión:

Tiempo 1
FORMA 1: SELECT count(ID) from Pedidos WHERE horaPedido>HoraActual -2 seg (tabla con todos los pedidos)

vs

FORMA 2: SELECT estado from DBtempo WHERE estado='1' and HoraActual>HoraPedido
(tabla de 1 registro
estado=1 = nuevos registros)

Tiempo 2
en el primer caso: si hay registros
en el segudo caso: si estado='1'
hace la siguiente operación

SELECT from Pedidos WHERE horaPedido>HoraActual -2 seg

si fuera de esta manera, en el segundo caso la busqueda al ser de un solo registro seria mas rápida, de igual manera se tiene que hacer una consulta para saber si hay nuevos pedidos en ese tiempo. Estoy en lo cierto o por favor corríjanme si me equivoco.
Saludos

Chris 30-03-2012 23:18:34

No entiendo exactamente tus comparaciones.

Pero la consulta que yo he utilizado es está:
Código SQL [-]
select p.id from pedidos id where fecha_tomado > :ultima_fecha_que_tengo

Es adaptada a tu escenario. Por otro lado, no sé si será la consulta más eficiente, pero sí es eficiente.

La anterior consulta te devolverá una lista de identificadores de los pedidos que han sido tomados luego de la última fecha y hora que tienes. Luego de hacer el "refresh" recuerda actualizar la variable `ultima_fecha_que_tengo´.

Saludos.

carlo_acp 30-03-2012 23:32:19

OK Cris, comprendi, gracias por tu ayuda, ya lo estoy implementando, ahora las consultas las estoy haciendo por un SP y los resultados a traves de un SP con SELECT LIST() para que me envie todos los nuevos pedidos, ase sea mas rapido. Gracias a todos por sus comentarios

Al González 31-03-2012 01:48:34

No sé si lo estás haciendo también, pero además de lo que te han dicho, recuerda que una de las ventajas de TClientDataSet es que puedes agregarle registros contenidos en otro TClientDataSet. De esa manera, puedes hacer esto:
Código Delphi [-]
CDS1.AppendData (CDS2.Data, True)
Donde CDS1 podría ser el conjunto de datos que muestra la totalidad de los registros y CDS2 el que se trae solamente los nuevos registros de la base de datos. Así, cada cierto tiempo irás acumulando en CDS1 los nuevos registros generados en la tabla.

Saludos.

Al González. :)

Neftali [Germán.Estévez] 02-04-2012 16:34:03

Cita:

Empezado por carlo_acp (Mensaje 428958)
Gracias Neftali, entonces el timer haria consultas a esa tabla si hubo modificaciones y si las hubo, hace la consulta a la tabla con los registros actualizados para actualizar su grid. lo entiendo asi?

De nada.
Sí, a eso me refería. Hacer consultas sobre una tabla muy pequeña que está vacía o con pocos registros y que además no se utiliza en más lugares de la aplicación (de esa forma no entorpeces otros trabajos).

Cita:

Empezado por Chris (Mensaje 428966)
No, no se trata de utilizar una tabla adicional. Se trata de utilizar la misma tabla de pedidos que ya tienes. Por ejemplo:

Realmente yo sí me refería a lo que comenta carlo_acp; Se trata de utilizar una pequeña tabla auxiliar para no hacer consultas sobre la tabla de pedidos; Por las varias razones que ya he explicado.
Una consulta como la que tú comentas, reduce el tiempo, pero aun así estás atacando sobre una tabla crítica (algunas veces cuando no es necesario). Si el tema de tiempo es crítico, el usar una tabla intermedia, te asegura que sólo consultarás la de pedidos cuando hay algo nuevo.

Haces más consultas, pero como contrapartida, sobre la tabla crítica son las mínimas necesarias, el resto son sobre una tabla auxiliar que puede tener 1 o 2 campos y 1 o 2 registro (mínima expresión y máxima rapidez).

carlo_acp 02-04-2012 22:34:44

Bueno amigos gracias por sus aportes, lo resolví de esta manera y lo comparto con uds:
1. En el form puse un temporizador de 2 segundos

2. he creado un tabla MovEsta con los campos
ESTA = 0(sin pedidos pendientes) 1(con pedidos pendientes)
HORAING tipo TIMESTAMP (pone la hora en la que se hizo el pedido)

3. en la tabla de Pedidos MovPediD un campo llamado HORAP tipo TIMESTAMP (con la hora del pedido)
4. he creado un TRIGGER en AfterPost de la Tabla de Pedidos
UPDATE movEsta set esta='1', horaIng='now' ;

5. un SP llamado NuevosPedidos

Código SQL [-]
CREATE OR ALTER PROCEDURE NUEVOS_PEDIDOS 
returns (
    lista1 varchar(1000),
    lista2 varchar(1000),
    lista3 varchar(1000))
as
declare variable esta varchar(1);
declare variable horaing timestamp;
declare variable horaact timestamp;
begin
    /* verificar si existe pedidos nuevos */
    SELECT esta, HoraIng, HoraAct from MovEsta
    WHERE esta='1'
    INTO :esta, :horaing,:HoraAct;

    /* si existen nuevos pedidos, devolver listado del pedido */
    if (esta='1') then
        begin
        SELECT list(p.cdpl , ','), list(d.nomb, ','), list(mesa, ',')
        FROM MovPediD P
        LEFT OUTER JOIN DbPlato D ON d.cdpl=P.cdpl
        WHERE P.horaP > :horaAct and P.horaP <=:horaIng
        into :Lista1, :lista2, :lista3 ;
        /* marcar MovEsta.esta=0 y horaAct =now para que ya no tenga pedidos pendientes */
        /* hasta la hora en que se esta actualiando a cocina */
        update MovEsta set esta='0', horaAct='now' ;
    end
 
end

El funcionamiento es de la siguiente manera: cuando realizan el pedido al momento de grabar en la tabla MovPediD, actualiza a MovEsta en el campo ESTA='1' y el HORAING='now'.
El timer en el form, hace la llamada al SP NuevosPedidos y se analiza lo que devuelve:

Código Delphi [-]
   if dat.spNuevos_Pedidos.Params[0].AsString<>'' then
      begin // agregar a cds nuevos pedidos
      CargarNuevosPedidos; /* separar las listas enviadas,  separadas por comas 
                           y crear los registros en el CDS que se muestra en el grid
   end;
Y este es el procedimiento;

Código Delphi [-]
procedure CargarNuevosPedidos;
var xNombre :  string;
begin
   // distribuyendo para cargarlo por registros a cds
   while pos(',', xNombre)>0 do
      begin
      Dat.cdsTempo.Append;
      dat.cdsTempoNOMBRE.AsString:= copy(xNombre, 1, pos(',', xNombre)-1);
      dat.cdsTempo.Post;
      xNombre:= trim(copy(xNombre, pos(',',xNombre)+1, 1000));
   end;
   Dat.cdsTempo.Append;
   dat.cdsTempoNOMBRE.AsString:= xnombre;
   dat.cdsTempo.Post;
end;

de esta forma me esta funcionando, estoy analizando otras opciones pero por lo pronto esta corriendo la aplicacion como se queria. Lo comparto aver si le sirve a alguien

Gracias Neftaly por la idea y a todos amigos,
Saludos

PepeLolo 12-04-2012 20:17:29

Si usas Interbase en cualquiera de sus versiones, tienes el componente "TIBEvents" que recibe las notificaciones que se han producido en la BBDD.
El uso es muy simple, no necesitas "timer" ni nada parecido, solo este componente que espera una notificación de los eventos que tengas registrados en el componente, ejemplo:

En el trigger de la entidad que quieres que realice la notificación, tienes que poner lo siguiente:

Código SQL [-]
CREATE TRIGGER PR_OP_BU1 FOR PR_OP
ACTIVE BEFORE UPDATE POSITION 1
....
.......
    POST_EVENT 'OP_MOD';  /* Avisar a tu aplicación que existen cambios en la entidad para que realice la actualización automáticamente */

y en el componente TIBEvents lo siguiente:
Código Delphi [-]
With IBEventos do
 begin
   AutoRegister := True;
   DataBase := TuBBDD;
   Events -> 'picha para que salga el asistente y añade tu evento.  "OP_MOD" es el nombre del mio.
 end;

Doble click sobre el evento "onEventAlert":

Código Delphi [-]
procedure Tdm.IBEvtBackupEventAlert(Sender: TObject; EventName: String;
  EventCount: Integer; var CancelAlerts: Boolean);
(*
Fecha : 08/09/2011
Autor : JMCDM
Comentario :
     Detectar modificaciones en la OP
*)
begin
   // Evento lanzado por modificación de OP
   if EventName = 'OP_MOD' then                      // 'OP_MOD' Nombre del evento de BBDD que capturas. 
     frMain.Alerta(OP_MOD);                             // Sentencia que quieres ejecutar.
end;

Ya ta eso es todo.

carlo_acp 12-04-2012 23:54:25

Hola PepeLolo, Yo utilizo Firebird con las DbExpress, interesante tu idea, voy a ver si existe lo mismo en firebird para implementarlo y lo comento como me fue, gracias por la idea.


La franja horaria es GMT +2. Ahora son las 23:19:59.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi