Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Crear campos calculados en consulta (https://www.clubdelphi.com/foros/showthread.php?t=71029)

GerTorresM 25-11-2010 17:07:26

Crear campos calculados en consulta
 
Hola a Tod@s:


Bien la situación del día de hoy es la siguiente:

Estoy trabajando con delphi 6 y como base de datos M$ Acce$$, quiero adicionar una serie de campo calculados en una consulta, ésto son de diversos tipos, la pregunta es como la puedo hacer.

agradezco de antemano el tiempo dedicado y la colaboración que me puedan prestar.


gertorresm
Colombia

cloayza 25-11-2010 19:46:29

Si los calculos no son demasiado complejos los puedes especificar en la misma consulta.

Por ejemplo:

Código SQL [-]
SELECT Precio, Iva,  (Precio*Iva) as Impuestos, (Precio+0.05) as Descuento
FROM TABLA

Espero te sirva, de lo contrario da un ejemplo de los calculos que requieres.

Saludos

GerTorresM 25-11-2010 22:53:17

Crear Campo Calculado
 
Gracias cloayza por tu tiempo la idea que tengo es la siguiente

Código Delphi [-]
function CrearCampoCalculado (pNombreCampo, pTipoCampo : String;  pDataset : TDataset) : TField;
var Campo_local : TField;
begin
  //Crea los campos Campos
  Campo_local := TFloatField.Create(pDataSet);
  Campo.FieldName:= pNombreCampo;
  Campo.FieldKind:= fkCalculated;
  Campo.DataSet:= pDataset;
  Result:= Campo_local;
end;

pero poder a su vez indicar en la función el tipo de dato que ha de manejar el campo ej.

TStringField,TSmallIntField, TIntegerField,
TBooleanField, TFloatField, TCurrencyField,
TDateField, TTimeField,TDateTimeField,
TVarBytesField, TBlobField, TMemoField,
TWordField, TBCDField,TBytesField
TGraphicsField, TAutoIncField,TNumericField

pero no que tipo de dato colocar en los párametros de entrada, lo que denomina pTipoCampo, es decir pTipoCampo que tipo de dato debe pedir, ya que a partir de dicho parámetro de entrada debo platear el constructor tal como lo vez de la línea de código

Código Delphi [-]
 TFloatField.Create(pDataSet);

muchas gracias de antemano



gertorresm
Colombia

defcon1_es 26-11-2010 12:31:11

Hola. ¿Te refieres a algo parecido a esto?

Código Delphi [-]
function CrearCampoCalculado (pNombreCampo: String;  pDataset : TDataset; pTipoCampo: Integer) : TField;
var Campo_local : TField;
begin
  //Crea los campos Campos
  case pTipoCampo of
    0: Campo_local := TFloatField.Create(pDataSet);
    1: Campo_local := TIntegerField.Create(pDataSet);
  //.... aqui pones el resto de tipos
  end;
  Campo.FieldName:= pNombreCampo;
  Campo.FieldKind:= fkCalculated;
  Campo.DataSet:= pDataset;
  Result:= Campo_local;
end;

Estifmauin 26-11-2010 13:26:37

Esta solución es buena, pero tiene una carencia: no tiene en cuenta que ya pueden existir campos en el dataset.
En mi caso, suelo añadir campos calculados a datasets "reales" que ya contiene campos persistentes. Un ejemplo es construir un mensaje de estado a partir de los valores de ciertos campos, y así mostrar en un grid directamente ese mensaje.
En ese caso, hay que respetar los campos existentes y luego añadirle los nuevos calculados:

Código Delphi [-]
procedure CrearCampoCalculado(DataSet :TDataSet; nombrecampo :string; tipo :TFieldType; longi :integer);
var
    f: TField;
    i: Integer;
begin
try
    //es necesario que antes estén definidos todos los campos persistentes
    DataSet.FieldDefs.Update;
    for i:=0 to DataSet.FieldDefs.Count - 1 do
        if DataSet.FindField(DataSet.FieldDefs[i].Name) = nil then
            DataSet.FieldDefs.Items[i].CreateField(DataSet);
    //ahora ya podemos comprobar el nuevo campo
    //antes comprobamos la existencia de otro campo con el mismo nombre
    if DataSet.FindField(nombrecampo) = nil then begin
        case tipo of
            ftString: f:=TStringField.Create(DataSet);
            ftInteger: f:=TIntegerField.Create(DataSet);
            ftBoolean: f:=TBooleanField.Create(DataSet);
            //contempla aqui todos los tipos que necesites
            //lógicamente, dependerán de la bbdd que uses
            else ShowMessage('Tipo de campo no contemplado: '+GetEnumName(TypeInfo(TFieldType),integer(tipo)));
        end;
        if f <> nil then begin
            f.DataSet:=DataSet;
            f.Name:=DataSet.Name+nombrecampo;
            f.FieldName:=nombrecampo;
            f.DisplayLabel:=nombrecampo;
            if tipo = ftString then
                f.Size:=longi;
            f.Calculated:=true;
        end;
    end
    else ShowMessage('Ya existe un campo de nombre "'+nombrecampo+'"');
except on e:exception do
    ShowMessage(e.Message);
end;
end;

Nota: la función GetEnumName requiere uses TypInfo;

Y llamo a esa función en el evento BeforeOpen:

Código Delphi [-]
procedure tDatasetBeforeOpen(DataSet: TDataSet);
begin
CrearCampoCalculado(DataSet, 'temporal_entero', ftInteger,0);
CrearCampoCalculado(DataSet, 'temporal_texto', ftString,20);
end;

Espero que ayude.

roboflekto 26-11-2010 18:20:51

Es suena interesante, voy a probarlo ahora mismo. Pero por qué dices es de:
//lógicamente, dependerán de la bbdd que uses
???
¿No se trata de campos calculados, es decir que no están en la base de datos?
y otra cosa: ¿por qué en el evento BeforeOpen? ¿No se puede usar en el AfterOpen?

Saludos a todos.

Estifmauin 26-11-2010 18:32:45

Cita:

Empezado por roboflekto (Mensaje 383400)
Es suena interesante, voy a probarlo ahora mismo. Pero por qué dices es de:
//lógicamente, dependerán de la bbdd que uses
???
¿No se trata de campos calculados, es decir que no están en la base de datos?
y otra cosa: ¿por qué en el evento BeforeOpen? ¿No se puede usar en el AfterOpen?

Saludos a todos.

Bueno... lo de que depende de la bbdd que uses es un comentario que se me ha traspapelado: ignóralo. Es evidente que tienes razón.

Respecto a lo de llamar a la función en el evento AfterOpen, es cierto que parece lo más obvio, pero cuando hice mis pruebas daba problemas, así que cogí la mecánica de hacerlo en Before.
Te animo a que lo pruebes, y si se puede hacer, lo compartas con nosotros.

GerTorresM 29-11-2010 12:33:17

Tema Cerrado
 
Muchas gracias a tod@s:


Con sus aportes ya arme la función que se requeria


Agradezco de antemano su tiempo y aporte



gertorresm
colombia

rchavezh 01-03-2011 21:38:19

Al parecer el orden de asignacion en delphi 2010 afecta por lo que actualizo la funcion

Código Delphi [-]
CrearCampoCalculado(DataSet :TDataSet; nombrecampo :string; tipo :TFieldType; longi :integer);
var
    f: TField;
    i: Integer;
begin
try
    //es necesario que antes estén definidos todos los campos persistentes
    DataSet.FieldDefs.Update;
    for i:=0 to DataSet.FieldDefs.Count - 1 do
        if DataSet.FindField(DataSet.FieldDefs[i].Name) = nil then
            DataSet.FieldDefs.Items[i].CreateField(DataSet);
    //ahora ya podemos comprobar el nuevo campo
    //antes comprobamos la existencia de otro campo con el mismo nombre
    if DataSet.FindField(nombrecampo) = nil then begin
        case tipo of
            ftString: f:=TStringField.Create(DataSet);
            ftInteger: f:=TIntegerField.Create(DataSet);
            ftFloat: f:=TFloatField.Create(DataSet);
            ftBoolean: f:=TBooleanField.Create(DataSet);
            //contempla aqui todos los tipos que necesites
            //lógicamente, dependerán de la bbdd que uses
            else GeneraLog('Tipo de campo no contemplado: ');
        end;
        if f <> nil then begin
            f.Name:=DataSet.Name+nombrecampo;
            f.FieldName:=nombrecampo;
            f.DisplayLabel:=nombrecampo;
            if tipo = ftString then
                f.Size:=longi;
            f.Calculated:=true;
            f.FieldKind := fkCalculated;
            f.DataSet:=DataSet;
        end;
    end;
except on e:exception do
    GeneraLog(e.Message);
end;

rgstuamigo 01-03-2011 22:08:43

Agradecemos tu colaboracion y tu actualizacion...:)
Nada más solicitarte que para que el código se vea bonito, la próxima ves utilices las etiquetas de código disponibles para una mejor estética.;), sin olvidar tambien que le dés una buena leida a nuestra Guía de Estilo si no lo has hecho aún...:)
Saludos.. y muchas gracias por tu colaboracion....:)

Al González 01-03-2011 23:30:14

Hola.

Me gustaría agregar que podemos prescindir del Case empleando la variable global DefaultFieldClasses:
Código Delphi [-]
// Tipo es una constante ftXXX
If DefaultFieldClasses [Tipo] <> Nil Then
  NuevoCampo := DefaultFieldClasses [Tipo].Create (DataSet)
Else
  // error

Respondiendo a roboflekto:
Cita:

Empezado por roboflekto (Mensaje 383400)
//lógicamente, dependerán de la bbdd que uses
???
¿No se trata de campos calculados, es decir que no están en la base de datos?
y otra cosa: ¿por qué en el evento BeforeOpen? ¿No se puede usar en el AfterOpen?

Más bien dependerá de los tipos de campos que soporte el componente conjunto de datos en cuestión.

Es más adecuado BeforeOpen que AfterOpen, para que al abrirse el conjunto de datos éste destine espacio en los buffers de registros para los nuevos campos calculados. De hecho hay una validación en la clase TField que impide agregar un nuevo campo a un conjunto de datos abierto.*

Saludos.

Al González. :)

*Off topic:
Tal validación puede ser burlada para crear campos calculados que no consuman memoria en los registros. Comento esto porque a alguien puede resultarle interesante como para abrir otro tema al respecto.


La franja horaria es GMT +2. Ahora son las 19:25:11.

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