Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Problemas copiando TIBQuery a TClientDataSet (https://www.clubdelphi.com/foros/showthread.php?t=63313)

Bauhaus1975 09-02-2009 09:37:20

Problemas copiando TIBQuery a TClientDataSet
 
Hola de nuevo,

Estoy tratando de crear una función para copiar el contenido de un TIBQuery a un TClientDataSet, donde tengo unos problemillas:

La primera cuestión es el error que obtengo al tratar de trabajar con el TClientDataSet en tiempo de ejecución. Creo el objeto, pero al tratar de hacer un open para insertar un registro obtengo el error 'Missing data provider or data packet'. Y sabemos que trabajando en diseño no hay problemas, además lo he podido comprobar -con este ejemplo aquí publicado-

La segunda cuestión, es que funcione la función que adjunto, pues me he basado en una que rodaba por el foro, pero usando TDataSet, el cual que yo sepa no me sirve pues hay que usar una clase hija de TDataSet.

Esta función me es necesaria para tener un paquete de datos disponible tras realizar la query (previamente, y solo una vez) puesto que a veces necesito alimentar varios objetos con el resultado, o tener los datos disponibles sin molestar a la BBDD una y otra vez. Seguramente ya os habréis encontrado alguno con este tema antes.

Aquí va la función que estoy tratando de hacer:

Código Delphi [-]
function cloneQuery(ibQuery:TIBQuery):TClientDataSet;
var
    Campo :string;
    i :integer;
    tabla : TClientDataSet;
begin
    tabla:= TClientDataSet.Create(nil); // Creamos clientdataset y definimos propiedades
    tabla.ProviderName := '';
    tabla.PacketRecords := -1;
    tabla.StoreDefs := true;
    tabla.Open; // Aquí obtenemos el error: "Missing data set provider..."
    // En esta sentencia se ejecuta la query que traerá los datos
    with ibQuery do
        begin
        Open;
        while not EOF do // Iteramos para ir insertando cada registro
          begin
          tabla.Append;
          for i := 0 to FieldCount-1 do
            begin
            Campo := Fields[i].FieldName;
            tabla[Campo] := ibQuery[Campo];
          end;
          tabla.Post;
          Next;
        end;
        Close;
    end;
    tabla.Close;
    result := tabla;
end;

Muchas gracias y un saludo.

defcon1_es 09-02-2009 13:08:18

Hola.
Prueba esto:
Código Delphi [-]
function cloneQuery(ibQuery:TIBQuery):TClientDataSet;
var
    Campo :string;
    i :integer;
    tabla : TClientDataSet;
begin
    tabla:= TClientDataSet.Create(nil); // Creamos clientdataset y definimos propiedades
    tabla.ProviderName := '';
    tabla.PacketRecords := -1;
    tabla.StoreDefs := true;
    tabla.Options := tabla.Options + [poAllowCommandText];
    tabla.CommandText := ibQuery.SQL.Text;
    tabla.Open; // Aquí obtenemos el error: "Missing data set provider..."
    // En esta sentencia se ejecuta la query que traerá los datos
    with ibQuery do
        begin
        Open;
        while not EOF do // Iteramos para ir insertando cada registro
          begin
          tabla.Append;
          for i := 0 to FieldCount-1 do
            begin
            Campo := Fields[i].FieldName;
            tabla[Campo] := ibQuery[Campo];
          end;
          tabla.Post;
          Next;
        end;
        Close;
    end;
    tabla.Close;
    result := tabla;
end;

Al González 09-02-2009 16:51:37

¡Hola!

Lo que buscas es perfectamente viable pasando la información del Query al TClientDataSet de la forma habitual: usando un TDataSetProvider.

Pero si aún lo quieres hacer con la función que has puesto, sólo tendrías que cambiar la instrucción Open por CreateDataSet.

Pruébalo y nos dices cómo te funciona.

Una cosa más: no hagas el Close al conjunto de datos cliente, porque eso causará que se pierda todo su contenido.

Saludos.

Al González. :)

Bauhaus1975 09-02-2009 20:33:13

Muchas gracias por vuestro tiempo y ayuda.
He probado vuestros consejos y voy con las respuestas:

Cita:

Empezado por defcon1_es (Mensaje 337547)
tabla.Options := tabla.Options + [poAllowCommandText];
tabla.CommandText := ibQuery.SQL.Text;

Parece que no funciona. Además parece que el TClientDataSet no tiene la propiedad 'options'.

Cita:

Empezado por Al González (Mensaje 337547)
¡Hola!
Pero si aún lo quieres hacer con la función que has puesto, sólo tendrías que cambiar la instrucción Open por CreateDataSet.

Sí, prefiero hacerlo con la función puesto que creo que es más 'portable' a mis casos... He probado cambiar Open por CreateDataSet como dices, pero ahora el error (casi me lo esperaba) es: 'No fields defined'.
¿Tengo que definir los campos previamente, con fielddefs?
¿Hay alguna forma de hacerlo automaticamente, es decir que lo coja del resultado de la query?

Otra cosa que me ronda la cabeza es lo que comentas de no poder cerrar ¿Te refieres al clientdataset?... puf, entonces la funcionalidad queda bastante reducida, ¿no?
A ver qué aconsejais... esto está apunto de salir, y esta función es de esas a usar en todo proyecto...

Un saludo.

Al González 10-02-2009 01:18:22

Cita:

Empezado por Bauhaus1975 (Mensaje 337587)
prefiero hacerlo con la función puesto que creo que es más 'portable' a mis casos... He probado cambiar Open por CreateDataSet como dices, pero ahora el error (casi me lo esperaba) es: 'No fields defined'.
¿Tengo que definir los campos previamente, con fielddefs?

Ah, claro, el conjunto de datos cliente (TClientDataSet) debe tener ya los campos que usará (lista de objetos TField en la propiedad Fields) o al menos sus definiciones (propiedad FieldDefs). Olvidé ese pequeño detalle.

Cita:

Empezado por ayuda de Delphi
If the FieldDefs property contains values, these values are used to create field definitions. Otherwise the Fields property is used. One or both of these properties must contain values in order to create a dataset. If neither property is set, CreateDataSet raises an exception.


Cita:

Empezado por Bauhaus1975 (Mensaje 337587)
¿Hay alguna forma de hacerlo automaticamente, es decir que lo coja del resultado de la query?

Desafortunadamente no existe una solución nativa que te copie (clone) los objetos campos de un conjunto de datos en otro. Es medianamente laborioso implementarla con un ciclo For que recorra la propiedad Fields del conjunto de datos origen (el TIBQuery, en este caso) y por cada campo cree una instancia de la misma clase en el conjunto de datos destino (el nuevo TClientDataSet), además de asignar las propiedades de cada campo copiado (al menos las esenciales como FieldName, Size, etc. y otras que requieras como DisplayFormat, Currency, etc.).

Si estás usando Delphi 7 o 2007 y no tienes inconveniente en emplear una de mis funciones, en GH Freebrary encontrarás un procedimiento llamado ghCopyFields que realiza esa tarea de "reproducción de campos". Tu función quedaría más o menos así (eliminé las sentencias innecesarias):

Código Delphi [-]
Uses
  GHFData;
...
function cloneQuery(ibQuery:TIBQuery):TClientDataSet;
var
    Campo :string;
    i :integer;
    tabla : TClientDataSet;
begin
    tabla:= TClientDataSet.Create(nil); // Creamos clientdataset y definimos propiedades
    ghCopyFields (ibQuery, tabla);  // Le creamos los mismos campos que a ibQuery
    tabla.CreateDataSet;  // Abrimos el conjunto de datos vacío

    // En esta sentencia se ejecuta la query que traerá los datos
    with ibQuery do
        begin
        Open;
        while not EOF do // Iteramos para ir insertando cada registro
          begin
          tabla.Append;
          for i := 0 to FieldCount-1 do
            begin
            Campo := Fields[i].FieldName;
            tabla[Campo] := ibQuery[Campo];
          end;
          tabla.Post;
          Next;
        end;
        Close;
    end;

    { Este es el "Close" que sugiero quitar.  Si acabas de crear y rellenar
      el clon, cerrarlo antes de usarlo no tiene sentido.  Ciérralo
      después, fuera de esta función, cuando ya no vayas a usarlo :). }
//    tabla.Close;

    result := tabla;
end;


Cita:

Empezado por Bauhaus1975 (Mensaje 337587)
Otra cosa que me ronda la cabeza es lo que comentas de no poder cerrar ¿Te refieres al clientdataset?... puf, entonces la funcionalidad queda bastante reducida, ¿no?

Me refiero al Close prematuro que comenté arriba, en la función. Lo normal sería cerrar el nuevo conjunto de datos cuando ya no lo necesites (mejor aún, destruirlo por completo), pero no cuando acabas de crearlo. Lo usual sería algo como esto:
Código Delphi [-]
MiCDS := cloneQuery (QueryX);

Try
  // Uso de MiCDS
Finally
  MiCDS.Free;
End;

Espero te sirva lo escrito y mi código. Coméntanos cómo te resulta.

Un saludo.

Al González. :)

Bauhaus1975 10-02-2009 09:37:32

Hola y gracias de nuevo Al González por tus 'nutridas' respuestas.
Tendré en cuenta la librería que propones, pero seguí probando con la función y se me ocurrió que si ejecuto la query y luego copio los FieldDefs, mantedría la estructura de campos. Así con este enfoque me ha funcionado perfectamente, sólo había un error: La sentencia tabla.Append tuve que cambiarla por tabla.Insert para que añadiera cada nuevo registro.

En cuanto al cierre del DataSet. Si cierro el data set ¡no se pierden los registros! lo he vuelto a abrir y ahí, estan los puedo recorrer y verificar uno a no. Por tanto esto esto funciona según he probado:

Código Delphi [-]
// Suponemos QueryX como el TIBQuery con el SQL listo para ejecutar
lista:= cloneQuery(QueryX); // Realizamos query y copia del data set según nuestra función.
while not (lista.EOF) do
begin
       showmessage('ID del registro'+lista.FieldByName('ID'));
       lista.Next;
end;
lista.Close;
lista.Open;
lista.First;
while not (lista.EOF) do
begin
        showmessage('ID del registro'+lista.FieldByName('ID'));
        lista.Next;
end;

Y aquí va como ha quedado la ULTIMA versión de la función que hemos estado trabajando:

Código Delphi [-]
function TEquipoCompTemp.cloneQuery(ibQuery:TIBQuery):TClientDataSet;
var
    Campo :string;
    i :integer;
    tabla : TClientDataSet;
begin
    tabla := TClientDataSet.Create(nil);
    tabla.ProviderName := '';
    tabla.PacketRecords := -1;
    tabla.StoreDefs := true;
    // Realizamos la query
    ibQuery.Open();
    // Copiamos estructura de campos
    tabla.FieldDefs := ibQuery.FieldDefs;
    // Abrimos DataSet
    tabla.CreateDataSet;
    with ibQuery do
        begin
        while not EOF do
          begin
          tabla.Insert; // Hemos cambiado Append por Insert
          for i := 0 to FieldCount-1 do
            begin
            Campo := Fields[i].FieldName;
            tabla[Campo] := ibQuery[Campo];
          end;
          tabla.Post;
          Next;
        end;
        Close;
    end;
    result := tabla;
end;

Un saludo.

Bauhaus1975 17-02-2009 09:33:42

Añadir sólo un apunte:

Cita:

Empezado por Bauhaus1975 (Mensaje 337638)
Así con este enfoque me ha funcionado perfectamente, sólo había un error: La sentencia tabla.Append tuve que cambiarla por tabla.Insert para que añadiera cada nuevo registro.

Esto ha sido un 'cuele' por otras causas. Funciona perfectamente Insert y Append no hagan caso.

Por lo demás la función funciona perfectamente y el dataset puede abrirse y cerrarse que los datos siguen ahí.

Saludos.


La franja horaria es GMT +2. Ahora son las 00:52:19.

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