Domanda

sto ottenendo i dati utilizzando una query a Delfi, e vorrei aggiungere un campo calcolato alla query prima che corre. Il campo calcolato sta usando i valori nel codice così come la query, quindi non posso solo calcolare in SQL.

So di poter collegare un evento OnCalcFields per rendere realtà il calcolo, ma il problema è dopo l'aggiunta del campo calcolato non ci sono altri campi nella query ...

Ho fatto qualche ricerca e ha scoperto che tutti i defs campo sono creati ma i campi effettivi sono creati solo

if DefaultFields then
    CreateFields

I campi di default è specificato

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

Il che indicherebbe che se si aggiungono i campi che si ottiene solo i campi aggiunti.

Vorrei tutti i campi nella query così come il quelli che ho aggiungere.

Questo è possibile o devo aggiungere tutti i campi che sto usando come bene?

È stato utile?

Soluzione

Delphi ha ora la possibilità di combinare i campi generati automatici e campi calcolati: Data.DB.TFieldOptions.AutoCreateMode un'enumerazione di tipo TFieldsAutoCreationMode . In questo modo è possibile aggiungere i campi calcolati in fase di esecuzione. Francois ha scritto nella sua risposta come aggiungere un campo in fase di esecuzione.

Diverse modalità di TFieldsAutoCreationMode:

  • acExclusive

    Quando non ci sono campi persistenti a tutti, allora si creano campi automatici. Questa è la modalità predefinita.

  • acCombineComputed

    I campi automatici vengono creati quando l'insieme di dati non ha campi persistenti o ci sono solo campi persistenti calcolati. Questo è un modo conveniente per creare campi calcolati persistenti in fase di progettazione e di lasciare che il set di dati di creare campi di dati automatici.

  • acCombineAlways

    campi automatici per i campi del database verranno creati quando non ci sono campi persistenti.

Altri suggerimenti

Nulla vi impedisce di creare tutti i campi prima nel codice,
quindi aggiungere i campi calcolati.

È possibile utilizzare un "tipo violato", per usare le CreateFields protette:

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

o prendere in prestito qualche codice da 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]);

quindi creare i campi calcolati:

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

È necessario aggiungere tutti i campi in aggiunta al campo calcolato.

Una volta che si aggiunge un campo, è necessario aggiungere tutti i campi che si desidera nel set di dati.

Delphi chiama questo persistenti campi contro campi dinamici. Tutti i campi sono o persistente o dinamico. Purtroppo, non si può avere una miscela di entrambi.

Un'altra cosa da notare, dalla documentazione è

  

campi persistenti liste di componenti sono   memorizzati nell'applicazione, e non   cambia anche se la struttura di un   database sottostante un set di dati è   cambiato.

Quindi, fate attenzione, se in seguito si aggiungono ulteriori campi a una tabella, è necessario aggiungere i nuovi campi al componente. Stessa cosa con l'eliminazione di campi.

Se davvero non si vuole campi persistenti, c'è un'altra soluzione. Su qualsiasi griglia o di controllo che dovrebbe mostrare il campo calcolato, è possibile disegnare su misura. Ad esempio, molti controlli griglia hanno un evento OnCustomDraw. Si può fare il calcolo lì.

Se si è conoscere i propri campi da calcolare nomi in fase di esecuzione, è possibile utilizzare una cosa del genere.

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top