Pregunta

Estoy recibiendo datos mediante una consulta en Delphi, y me gustaría añadir un campo calculado a la consulta antes de que se ejecute. El campo calculado está utilizando valores de código, así como la consulta así que no puedo calcularlo en SQL.

Sé que puedo adjuntar un evento OnCalcFields para hacer realidad el cálculo, pero el problema es después de agregar el campo calculado no hay otros campos en la consulta ...

Hice algo de investigación y encontró que todos los defs de campo son creados pero los campos reales sólo se crean

if DefaultFields then
    CreateFields

Los campos por defecto se especifica

procedure TDataSet.DoInternalOpen;
begin
    FDefaultFields := FieldCount = 0;
    ...
end;

lo que indicaría que si agrega campos que sólo recibe los campos que agregó.

Me gustaría que todos los campos de la consulta, así como el que más me Agregar.

¿Es esto posible o tengo que añadir todos los campos que estoy usando así?

¿Fue útil?

Solución

Delphi tiene ahora la opción de combinar campos generados automáticas y campos calculados: Data.DB.TFieldOptions.AutoCreateMode una enumeración de tipo TFieldsAutoCreationMode . De esta manera usted puede añadir sus campos calculados en tiempo de ejecución. Francois escribió en su respuesta cómo agregar un campo en tiempo de ejecución.

Los diferentes modos de TFieldsAutoCreationMode:

  • acExclusive

    Cuando no hay campos persistentes en absoluto, entonces se crean campos automáticos. Este es el modo por defecto.

  • acCombineComputed

    Los campos automáticos se crean cuando el conjunto de datos no tiene campos persistentes o hay solamente se calculan campos persistentes. Esta es una manera conveniente de crear los campos calculados persistentes en tiempo de diseño y dejar que el conjunto de datos a crear campos de datos automáticos.

  • acCombineAlways

    campos automáticos para los campos de la base se crean cuando no hay campos persistentes.

Otros consejos

Nada le impide la creación de todos los campos primero en el código, España a continuación, añadir sus campos calculados.

Se puede utilizar un "Tipo de piratería", para usar las CreateFields protegidas:

type
  THackQuery = class(TADOQuery)
  end;
[...]
  MyQuery.FieldDefs.Update;
  THackQuery(MyQuery).CreateFields;

o tomar prestado algo de código de CreateFields:

  MyQuery.FieldDefs.Update;
  // create all defaults fields
  for I := 0 to MyQuery.FieldDefList.Count - 1 do
    with MyQuery.FieldDefList[I] do
      if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and
        not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then
        CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]);

a continuación, crear sus campos calculados:

  MyQueryMyField := TStringField.Create(MyQuery);
  with MyQueryMyField do
  begin
    Name := 'MyQueryMyField';
    FieldKind := fkCalculated;
    FieldName := 'MyField';
    Size := 10;
    DataSet := MyQuery;
  end;

Es necesario añadir todos los campos, además de su campo calculado.

Una vez que se agrega un campo, hay que añadir todos los campos que desee en el conjunto de datos.

Delphi llama a esto campos persistentes frente a campos dinámicos. Todos los campos son o persistente o dinámico. Por desgracia, no se puede tener una mezcla de ambos.

Otra cosa a la nota, de la documentación es

  

campos persistentes listas de componentes son   almacenado en la aplicación, y no hacer   el cambio incluso si la estructura de una   base de datos subyacente es un conjunto de datos   cambiado.

Por lo tanto, tenga cuidado, si más adelante añadir campos adicionales a una tabla, tendrá que añadir los nuevos campos para el componente. Lo mismo con la supresión de los campos.

Si realmente no quiere campos persistentes, no hay otra solución. En cualquier red o de control que debe mostrar el campo calculado, se puede dibujar a medida que. Por ejemplo, muchos controles cuadrícula tienen un evento OnCustomDraw. Usted puede hacer sus cálculos allí.

Si ha de ser conocer sus nombres campos calculados en tiempo de ejecución, puede utilizar algo por el estilo.

var
 initing:boolean;

procedure TSampleForm.dsSampleAfterOpen(
  DataSet: TDataSet);
var
 i:integer;
 dmp:tfield;
begin
if not initing then
 try
  initing:=true;
  dataset.active:=false;
  dataset.FieldDefs.Update;
  for i:=0 to dataset.FieldDefs.Count-1 do
  begin
   dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
   dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName;
   dmp.DataSet:=dataset;
   if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then
   begin
     dmp.Calculated:=true;
     dmp.DisplayWidth:=255;
     dmp.size:=255;
   end;
  end;
  dataset.active:=true;
 finally
  initing:=false;
 end;
end;

procedure TSampleForm.dsSampleAfterClose(
  DataSet: TDataSet);
var
 i:integer;
 dmp:TField;
begin
if not initing then
begin
 for i:=DataSet.FieldCount-1 downto 0 do
 begin
  dmp:=pointer(DataSet.Fields.Fields[i]);
  DataSet.Fields.Fields[i].DataSet:=nil;
  freeandnil(dmp);
 end;
 DataSet.FieldDefs.Clear;
end;
end;

procedure TSampleForm.dsSampleCalcFields(
  DataSet: TDataSet);
var
 tmpdurum,tmpOldDurum:integer;
begin
  if not initing then
    begin
      tmpDurum := dataset.FieldByName( 'state' ).AsInteger;
      tmpOldDurum:= dataset.FieldByName( 'oldstate' ).AsInteger;
      dataset.FieldByName( 'txtState' ).AsString := State2Text(tmpDurum);
      dataset.FieldByName( 'txtOldState' ).AsString := State2Text(tmpOldDurum);
    end;
end;

procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
 if dsSample.Active then
   dsSample.Close;
 dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
 dsSample.Open;
end;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top