Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Consulta muy sencilla, extrañamente lenta (https://www.clubdelphi.com/foros/showthread.php?t=56725)

Angel Fernández 26-05-2008 11:18:20

Consulta muy sencilla, extrañamente lenta
 
Saludos al foro.

Quería consultaros un problemilla muy tonto que tengo pero que no acierto a ver cual podría ser el origen. Me explico: Tengo una base de datos enorme donde se recogen datos de unos sensores. Tengo una tabla de sensores con la siguiente estructura:

Nombre del Campo Tipo Not null Explicación
-------------------------------------------------------------------------------------------------
IDSENSOR * INTEGER TRUE (Clave primaria,autoincremento)
IDPROYECTO INTEGER TRUE (Apunta a la tabla de proyectos)
IDTIPOSENSOR * INTEGER TRUE (Apunta a la tabla tiposensor)
REF CHAR(6) TRUE (Referencia corta) *
UBICACION VARCHAR(150) FALSE (Lugar donde se encuentra, no obligatorio)
NOMBRE * VARCHAR(50) FALSE (Nombre largo, no obligatorio)
ACTIVO SMALLINT TRUE (0 Inactivo, 1 Activo)
BUENAFUNCION SMALLINT TRUE (0 No va bien, 1 Va bien)

* = indica que hay índice ascendente.

Un ejemplo de los datos que contiene:

IDSENSOR IDPROYECTO IDTIPOSENSOR REF UBICACION NOMBRE ACTIVO BUENAFUNCION
------------------------------------------------------------------------------------------------------------------------------------------------
184 1 1 TEM001 Azotea SENSOR TEMPERATURA 001 1 1
185 1 1 TEM002 Azotea SENSOR TEMPERATURA 002 1 1
186 1 1 TEM003 Azotea SENSOR TEMPERATURA 003 1 1
187 1 1 TEM004 Azotea SENSOR TEMPERATURA 004 1 1
188 1 1 TEM005 Azotea SENSOR TEMPERATURA 005 1 1
189 1 1 TEM006 Azotea SENSOR TEMPERATURA 006 1 0
190 1 1 TEM007 Azotea SENSOR TEMPERATURA 007 1 1

Y así hasta 70 registros (setenta, menos de cien, no setenta mil, es decir, muy pocos registros).

La tabla Tiposensor tiene la siguiente estructura:

Nombre del Campo Tipo Not null
-------------------------------------------------------------------------------------------------
IDTIPOSENSOR * INTEGER TRUE (clave primaria, autoincremento)
TIPO_LARGO * VARCHAR(20) TRUE (Nombre largo, obligatorio)
TIPO* CHAR(3) TRUE (Nombre corto, obligatorio) *
ACTIVO SMALLINT TRUE (1=activo, 0=no activo) (No lo uso)
IDTABLADATOS * INTEGER TRUE (Apunta a otra tabla)
IDTIPODATO * INTEGER TRUE (Apunta a otra tabla)

* = indica que hay índice ascendente.

Esta tabla sólo tiene 6 registros:

IDTIPOSENSOR TIPO_LARGO TIPO ACTIVO IDTABLADATOS IDTIPODATO
----------------------------------------------------------------------------------------------------------------
0 INDEFINIDO NOT 1 0 0
1 TEMPERATURA EN ºC TEM 1 1 2
2 HUMEDAD HUM 1 1 1
3 LLUVIA LLU 1 2 4
4 ENCHARCAMIENTO ENC 1 2 5
5 TEMPERATURA EN ºK TEK 1 1 3

Y en fín, el problema:
Tengo un MDODataSet con el siguiente SelectSQL: 'select * from sensores order by ref' Esto me devuelve 70 registros y tarda ¡30 segundos!
Pero esta tardanza ocurre sólo la primera vez que se abre el MDODATASET; las siguientes veces tarda menos de 1 segundo que es lo lógico y esperable.
El MDODATASET tiene los campos de datos y además un campo lookup que apunta a la tabla TipoSensor para traer el tipo de sensor, y dos campos calculados que son del tipo booleano para convertir los campos ACTIVO y BUENAFUNCION al tipo true/false.

He probado de todo: quitar la apertura del mdodataset del evento formshow, quitar los campos calculados y lookup .... y ya no se me ocurre nada más.
Estoy desesperadico.

¿Alguna idea? ¿A alguien le ha pasado algo parecido? ¿Es un bug de los MDO?

Uso D7, Firebird 2.0 y componentes MDO.

Gracias de antemano.

PD: Perdón por el rollo.

Al González 26-05-2008 14:40:52

¡Hola!

Prueba si la conexión por sí misma tiene esa demora, abriéndola explícitamente antes de lanzar ninguna consulta.

Algo como "ConexionMDO.Open;" (desconozco los componentes MDO). Podría tratarse de un retardo provocado por un factor externo a la base de datos, como un contrafuegos o algo por el estilo (aunque me parece mucho tiempo).

También puedes verificar si te da tiempos similares abriendo la base de datos y ejecutando la consulta desde IB Expert o el programa administrativo que uses.

Por otro lado, puede que tu programa esté realizando (quizá desde algún evento) alguna operación que demora mucho tiempo cuando se abre la base de datos o ejecuta la consulta. Sería cuestión de revisar el código.

La depuración paso a paso (F7/F8) puede ayudarte a indagar en qué punto del código se está demorando la operación.

No dejes de retroalimentar el hilo pa' saber que jue.

Saludos.

Al. :)

juanelo 26-05-2008 15:40:26

Hola Angel,
Puedes revisar esta informacion
Saludos

luchifer 26-05-2008 17:33:49

Reevisa los drivers
 
Una vez tuve un problema similar trabajando con oracle y se debio a la configuracion del rollback del mismo.

No se que base de datos estes usando pero para que se te ponga tan lenta es que la conexion con esa base de datos esta usando los drives incorrectos por ejm si usas oracle en lugar de usar el driver de orcl estes usando el de odbc o mssql.

Otra posible causa es que tue equipo tenga virus, especificamente gusanos

Avisame como te fue

Casimiro Notevi 26-05-2008 19:28:44

La consulta debería ser instantánea, comprueba, tal y como te ha comentado Al González, depurar el programa paso a paso y asegurarte de que lo lento no es el abrir/conectar a la base de datos.

RolphyReyes 26-05-2008 22:25:04

Saludos.

Prueba a ver si te falta algún indice por el campo que vas a ordenar, si usas IbExpert te marcara si esta consulta utiliza un PLAN NATURAL y podrás poner el indice adecuado.

Hasta luego.

Angel Fernández 27-05-2008 09:54:15

Gracias a todos por vuestras intervenciones.
Hasta ahora todos los intentos son infructuosos. Como me indican Al y Casimiro he mirado el programa paso a paso y el parón se produce al hacer open.
Lo de los drivers que apunta luchifer no es porque uso firebird 2.0 y no tengo problema en otros programas.
Lo de un posible virus, hombre nunca se puede descartar eso, pero me llevo el programa a otro ordenador y produce el mismo resultado. Que haya el mismo gusano en dos ordenadores distintos es poco probable, a no ser que sea mi programa el que lo lleva. Acabo de pasarle el antivirus y nada.
Ahora mismo, lo único que se me ocurre es que sea un bug de los componentes MDO. Curiosamente, desde IBManager la consulta es rápida, y desde otro programa mío que no usa MDO sino FIBPLUS también es rápida. Y también curioso que todo este problema me lo da única y exclusivamente con una tabla, la de sensores, que sólo tiene 70 datos aunque estoy haciendo pruebas continuamente y estoy añadiendo y borrando datos. Y la lentitud se produce también si accedo a sensores desde un MDOQUERY.
Tengo otra tabla que recoge los datos de los sensores, con 5 millones de datos y consultas mucho más complicadas tardan 40 segundos.

Ahorita mismo estoy totalmente perdido. Sólo se me ocurre sustituir el MDO por FIBPLUS.
¡Ah! Lo que me apuntaba juanelo: he leído la información que me sugerías pero no he sacado nada en claro. Al parecer a aquella persona le pasaba lo mismo que a mí pero no logró solucionarlo o no lo indica.
Otro dato: la base de datos FB tiene extensión FDB y no GDB que, al parecer, da problemas.
Si consigo alguna solución, la comentaré.
Un saludo.

Angel Fernández 27-05-2008 09:56:16

Añadido:

Lo que comenta Rolphi de los índices ya lo he revisado y todos los campos por los que ordeno tienen índice.

Un saludo.

Al González 27-05-2008 15:18:10

¡Hola!

Te agradezco que hayas retroalimentado el hilo.

Cita:

Empezado por Angel Fernández (Mensaje 289285)
...Hasta ahora todos los intentos son infructuosos...he mirado el programa paso a paso y el parón se produce al hacer open....Sólo se me ocurre sustituir el MDO por FIBPLUS...

Compila el programa con la opción "Use Debug DCUs" (opciones del proyecto) y coloca un punto de ruptura (break point) en la sentencia que hace el "Open". Cuando llegue ahí el programa, usa la tecla F7 para entrar a ese método y luego combina F7 con F8 según convenga para navegar en los fuentes y ver en qué parte concreta de MDO/VCL se está demorando y por qué.

Esperamos tus comentarios.

Al.

Casimiro Notevi 27-05-2008 18:49:24

Y de paso puedes extraer un metadata de la base de datos y de los datos de esa tabla para que probemos con MDO también. Si puedes, claro.

Angel Fernández 28-05-2008 13:19:49

He sustituido los componentes MDO por FIBPLUS y hasta ahora, las pruebas que he hecho han sido satisfactorias, es decir, el problema parece haberse arreglado.
Sin embargo y por intentar arrojar un poco de luz a este asunto y poder ayudar a alguien que le pase lo mismo, he hecho lo que me apunta Al y he intentado depurar el programa paso a paso con F7, incluso dentro de los distintos componentes. Lamento decir que no lo he conseguido porque me meto en un berenjenal que no parece tener fin: entro en forms, system, mdoquery y un larguísimo etcétera de unidades y procesos. Al cabo de 15 minutos de estar dándole a F7 me he mareado, he pulsado F9 y ¡aparece el parón pero no sé dónde!
Sin duda es mi culpa pues mis conocimientos son muy escasos y estoy dando los segundos pasos con firebird (no los primeros, pero casi).

Cita:

Empezado por Casimiro Notevi (Mensaje 289399)
Y de paso puedes extraer un metadata de la base de datos y de los datos de esa tabla para que probemos con MDO también. Si puedes, claro.

Lo que son datos de la tabla de sensores y tipo de sensores creo que sí puedo darte (no puedo darte los datos que recogen los sensores pues esos pertenecen al cliente). Lo de metadata no sé muy bien qué es; con IBManager he hecho un export metadata de la tabla de sensores y me da algo así (perdón por el formateo de los datos):

RecId Field Name Description Field Type Domain Not Null Default Source Computed Source
1 IDSENSOR INTEGER RDB$64 True
2 IDPROYECTO INTEGER RDB$65 True
3 IDTIPOSENSOR INTEGER RDB$66 True
4 REF CHAR(6) RDB$67 True
5 UBICACION VARCHAR(150) RDB$68 False
6 NOMBRE VARCHAR(50) RDB$69 False
7 ACTIVO 0 Inactivo, 1 Activo SMALLINT RDB$70 True 1
8 BUENAFUNCION 0 No va bien. 1 va bien. SMALLINT RDB$13 True 1

Si lo que quieres es eso, puedo extraer esos datos de la tabla sensores, tipo de sensor y alguna otra a la que apunta sensores y enviártelos mejor formateados.

Gracias a todos por vuestra ayuda y un saludo.

Angel Fernández 28-05-2008 13:26:48

Añadido:

He copiado los mdodataset que me dan el problema y los he copiado a otra aplicación nueva. Estoy intentado discernir si el problema ocurre sólo con mi aplicación o es de los mdo.
El caso es que lo he probado y funciona bien, pero claro, acababa de probarlo en mi aplicación con lo cual ya no es la primera vez que se ejecuta.
Estoy dando palos de ciego... a ver si entre todos hallamos una solución.

Gracias otra vez.

Al González 28-05-2008 15:39:56

¡Hola!

Cita:

Empezado por Angel Fernández (Mensaje 289541)
...por intentar arrojar un poco de luz a este asunto y poder ayudar a alguien que le pase lo mismo, he hecho lo que me apunta Al y he intentado depurar el programa paso a paso con F7, incluso dentro de los distintos componentes. Lamento decir que no lo he conseguido porque me meto en un berenjenal que no parece tener fin: entro en forms, system, mdoquery y un larguísimo etcétera de unidades y procesos. Al cabo de 15 minutos de estar dándole a F7 me he mareado...

No subestimes la complejidad de la VCL. ;)

Hay que usar F8 también, con puro F7 demorarías varias semanas en encontrar el problema.

Creo que este otro mensaje puede orientarte al respecto:
http://www.clubdelphi.com/foros/show...22&postcount=4

Un saludo.

Al. :)

Angel Fernández 02-06-2008 11:21:33

Hola a todos. Por aquí de nuevo, con algo de retraso pero es que voy muy liado últimamente.

Casi he conseguido localizar dónde se produce el parón. Con F8 como me indicaba Al va mucho mejor que F7 que era para salir loco ;).

Al grano: he dicho "casi" porque tengo el procedimiento donde se produce el parón, pero no la línea. El procedimiento es el siguiente:
TMDOSQL.Prepare

Ahora no puedo pasarle otra vez el F8 porque el problema, recordad, era sólo para la primera vez. (Manda huevos).

En fín, aquí sigo, investigando...

Un saludo.

Al González 02-06-2008 15:29:43

Cita:

Empezado por Angel Fernández (Mensaje 290679)
...tengo el procedimiento donde se produce el parón, pero no la línea. El procedimiento es el siguiente:
TMDOSQL.Prepare...

Coloca un punto de ruptura en el Begin de ese método. Ejecuta el programa de manera normal y cuando tu consulta se ejecute y se detenga en ese punto de ruptura, continúa de nuevo con F8/F7 dentro de las sentencias de esa rutina.

Ya acorralaste a la anomalía, casi la has cazado. :cool:

Seguimos leyéndote.

Al.

Delfino 03-06-2008 19:46:29

Cita:

Pero esta tardanza ocurre sólo la primera vez que se abre el MDODATASET
No creo q la causa sean los MDO, a mi me paso una vez y la causa era el campo lookup, mas concretamente abrir la tabla lookup en un momento inadecuado..

Angel Fernández 07-06-2008 11:42:49

Hola, de nuevo por aquí con algo más de información.
Cita:

Empezado por Al González (Mensaje 290709)
Ya acorralaste a la anomalía, casi la has cazado. :cool:
Al.

Gracias Al por tu apoyo pero me temo que voy a defraudarte. He encontrado la línea de código que ocasiona el parón.
Concretamente es la unit MDOSQL.PAS procedure TMDOSQL.PREPARE (copio y pego directamente del editor para tratar de ubicar un poco mejor el problema):

Código Delphi [-]procedure TMDOSQL.Prepare;
var stmt_len: Integer; res_buffer: array[0..7] of Char;
type_item: Char;
begin if FCursor = '' then FCursor := Name + RandomString(8); CheckClosed; FBase.CheckDatabase; FBase.CheckTransaction; if FPrepared then exit; if (FSQL.Text = '') then MDOError(mdoeEmptyQuery, [nil]);
if not ParamCheck then FProcessedSQL.Text := FSQL.Text else PreprocessSQL; if (FProcessedSQL.Text = '') then MDOError(mdoeEmptyQuery, [nil]);
try Call(isc_dsql_alloc_statement2(StatusVector, DBHandle, @FHandle), True); {PROBLEMA --> } Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0, PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
{ After preparing the statement, query the stmt type and possibly
create a FSQLRecord "holder" } { Get the type of the statement } type_item := Char(isc_info_sql_stmt_type); Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item, SizeOf(res_buffer), res_buffer), True); if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then MDOError(mdoeUnknownError, [nil]);
stmt_len := isc_vax_integer(@res_buffer[1], 2);
FSQLType := TMDOSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
{ Done getting the type } case FSQLType of SQLGetSegment, SQLPutSegment, SQLStartTransaction: begin FreeHandle; MDOError(mdoeNotPermitted, [nil]);
end;
SQLCommit,
SQLRollback,
SQLDDL, SQLSetGenerator,
SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
SQLExecProcedure: begin { We already know how many inputs there are, so... } if (FSQLParams.FXSQLDA <> nil) and (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect, FSQLParams.FXSQLDA), False) > 0) then MDODatabaseError; FSQLParams.Initialize; if FSQLType in [SQLSelect, SQLSelectForUpdate,
SQLExecProcedure] then begin { Allocate an initial output descriptor (with one column) } FSQLRecord.Count := 1; { Using isc_dsql_describe, get the right size for the columns... } Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True); if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then begin FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld; Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True); end else if FSQLRecord.FXSQLDA^.sqld = 0 then FSQLRecord.Count := 0; FSQLRecord.Initialize; end;
end;
end;
FPrepared := True;
if not (csDesigning in ComponentState) then MonitorHook.SQLPrepare(Self); except on E: Exception do begin if (FHandle <> nil) then FreeHandle; raise;
end;
end;
end;


Y ya no sé qué mas hacer. Yo no soy capaz de resolver el problema ni aún sabiendo que está en esa línea.

Lo que dije en un post anterior que con FIBPLUS se solucionaba, no es del todo cierto. Se soluciona al acceder a la tabla sensores en un primer momento, pero extrañamente, aparece el parón con retardo.

Entiendo que debe ser algo relacionado con los registros que dependen de la tabla sensores. Hay que recordar que de esta tabla dependen dos tablas más que son las que almacenan los datos, y ahora mismo tienen más de 6 millones de datos entre las dos. Esto lo he deducido de una de mis múltiples pruebas a ciegas: he quitado los datos de las tablas de datos y el parón en la tabla de sensores desaparece (al menos así parece). ¿Es posible que un número muy grandes de datos de una tabla dependiente ocasione retardo en la tabla madre? Todo esto me da un dolor de cabeza tremendo y me temo que lo voy a dejar así.

De lo que apunta Delfino de un campo lookup abierto en un momento inadecuado, no creo que sea eso porque ya desactivé el campo lookup y el problema persistía.

Gracias a todos por vuestras aportaciones y en especial a Al que me ha animado a "cazar" la anomalía (aunque el cazador en este caso es un novato con un rifle de perdigones que no mataría ni un pollito). Si se te ocurre alguna otra prueba, Al, dímela y encantado la hago.

Un saludo.

Angel Fernández 07-06-2008 11:44:36

Perdón, al hacer vista previa de mensaje, el código delphi se veía bien, pero al enviar la respuesta definitiva me lo formatea mal.

Vuelvo a enviar el código delphi formateado:

Código Delphi [-]
procedure TMDOSQL.Prepare;
var
  stmt_len: Integer;
  res_buffer: array[0..7] of Char;
  type_item: Char;
begin
  if FCursor = '' then
    FCursor := Name + RandomString(8);
  CheckClosed;
  FBase.CheckDatabase;
  FBase.CheckTransaction;
  if FPrepared then
    exit;
  if (FSQL.Text = '') then
    MDOError(mdoeEmptyQuery, [nil]);
  if not ParamCheck then
    FProcessedSQL.Text := FSQL.Text
  else
    PreprocessSQL;
  if (FProcessedSQL.Text = '') then
    MDOError(mdoeEmptyQuery, [nil]);
  try
    Call(isc_dsql_alloc_statement2(StatusVector, DBHandle,
                                    @FHandle), True);
    Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
               PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);
    { After preparing the statement, query the stmt type and possibly
      create a FSQLRecord "holder" }
    { Get the type of the statement }
    type_item := Char(isc_info_sql_stmt_type);
    Call(isc_dsql_sql_info(StatusVector, @FHandle, 1, @type_item,
                         SizeOf(res_buffer), res_buffer), True);
    if (res_buffer[0] <> Char(isc_info_sql_stmt_type)) then
      MDOError(mdoeUnknownError, [nil]);
    stmt_len := isc_vax_integer(@res_buffer[1], 2);
    FSQLType := TMDOSQLTypes(isc_vax_integer(@res_buffer[3], stmt_len));
    { Done getting the type }
    case FSQLType of
      SQLGetSegment,
      SQLPutSegment,
      SQLStartTransaction: begin
        FreeHandle;
        MDOError(mdoeNotPermitted, [nil]);
      end;
      SQLCommit,
      SQLRollback,
      SQLDDL, SQLSetGenerator,
      SQLInsert, SQLUpdate, SQLDelete, SQLSelect, SQLSelectForUpdate,
      SQLExecProcedure: begin
        { We already know how many inputs there are, so... }
        if (FSQLParams.FXSQLDA <> nil) and
           (Call(isc_dsql_describe_bind(StatusVector, @FHandle, Database.SQLDialect,
                                        FSQLParams.FXSQLDA), False) > 0) then
          MDODatabaseError;
        FSQLParams.Initialize;
        if FSQLType in [SQLSelect, SQLSelectForUpdate,
                        SQLExecProcedure] then
        begin
          { Allocate an initial output descriptor (with one column) }
          FSQLRecord.Count := 1;
          { Using isc_dsql_describe, get the right size for the columns... }
          Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
          if FSQLRecord.FXSQLDA^.sqld > FSQLRecord.FXSQLDA^.sqln then
          begin
            FSQLRecord.Count := FSQLRecord.FXSQLDA^.sqld;
            Call(isc_dsql_describe(StatusVector, @FHandle, Database.SQLDialect, FSQLRecord.FXSQLDA), True);
          end else if FSQLRecord.FXSQLDA^.sqld = 0 then
            FSQLRecord.Count := 0;
          FSQLRecord.Initialize;
        end;
      end;
    end;
    FPrepared := True;
    if not (csDesigning in ComponentState) then
      MonitorHook.SQLPrepare(Self);
  except
    on E: Exception do
    begin
      if (FHandle <> nil) then
        FreeHandle;
      raise;
    end;
  end;
end;

Disculpad.

Angel Fernández 07-06-2008 11:47:14

Y ahora me olvido indicar la línea con el problema.
Es ésta:

Call(isc_dsql_prepare(StatusVector, TRHandle, @FHandle, 0,
PChar(FProcessedSQL.Text), Database.SQLDialect, nil), True);

Ruego a los moderadores perdonen mi torpeza (aunque en mi descargo diré que es un problema del botón "Vista Previa de Mensaje")

Delfino 08-06-2008 22:12:31

Aunque no estoy convencido de ello, intenta preparar la query antes de abrirla, la query tiene un metodo llamado Prepare,
por otra parte intenta remplazar el MDOQuery por un MDODataset asignando la string de la SQL de la primera a la SelectSQL de la segunda..
Has mirado si esta linea existe tb en los Fibplus?


La franja horaria es GMT +2. Ahora son las 20:56:51.

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