2010-03-26 12 views
7

Ricevo i dati utilizzando una query in Delphi e desidero aggiungere un campo calcolato alla query prima dell'esecuzione. Il campo calcolato utilizza valori nel codice e anche nella query, quindi non posso semplicemente calcolarlo in SQL.Aggiunta di un campo calcolato a una query in fase di esecuzione

so di poter collegare un OnCalcFields eventi per fare effettivamente 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 dei defs campo si creano ma i campi effettivi vengono creati solo

è specificato
if DefaultFields then 
    CreateFields 

campi predefiniti

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

Whi ch indicherà che se aggiungi campi, ottieni solo i campi che hai aggiunto.

Vorrei tutti i campi nella query AS BENE come quelli che aggiungo.

È possibile o devo aggiungere anche tutti i campi che sto utilizzando?

+0

Non capisco il motivo per cui non è possibile utilizzare i valori dal codice nel tuo SQL .... ho costruire istruzioni SQL dinamiche regolarmente che i valori d'uso del codice SQL .... – Leslie

+0

vedere la mia risposta per un nuovo modo di fare questo dal delhi Berlin –

+0

hai provato a PREPARARE la query? Potrebbe aver creato fielddefs (non ancora oggetti di campo) –

risposta

-1

Delphi ha ora la possibilità di combinare 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. Nella sua risposta, Francois ha scritto come aggiungere un campo in fase di runtime.

diverse modalità di TFieldsAutoCreationMode:

  • acExclusive

    Quando non ci sono campi persistenti a tutti, allora i campi automatici vengono creati. Questa è la modalità di default.

  • 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 i campi calcolati persistenti in fase di progettazione e lasciare che il set di dati crei campi di dati automatici.

  • acCombineAlways

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

+0

Sembra che questo consenta di combinare i campi calcolati per il tempo di progettazione e i campi dati di runtime. Hai testato/successo ciò che rivendichi nella risposta? –

+0

Questo non è compito di matematica in cui devi "provare" tutto. Il codice per creare un campo in fase di runtime è descritto in un'altra risposta –

+0

Bene, la documentazione non dice quello che rivendichi. –

3

È necessario aggiungere tutti i campi oltre al campo calcolato.

Dopo aver aggiunto un campo, è necessario aggiungere tutti i campi che si desidera nel set di dati.

Delphi chiama questi campi persistenti rispetto ai campi dinamici. Tutti i campi sono permanenti o dinamici. Sfortunatamente, non puoi avere una miscela di entrambi.

Un'altra cosa da notare, dalla documentazione è

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

Quindi, fare attenzione, se in seguito si aggiungono campi aggiuntivi a una tabella, sarà necessario aggiungere i nuovi campi al componente. Stessa cosa con l'eliminazione dei campi.

Se davvero non si desiderano campi persistenti, esiste un'altra soluzione. Su qualsiasi griglia o controllo che dovrebbe mostrare il campo calcolato, puoi disegnarlo su misura. Ad esempio, molti controlli griglia hanno un evento OnCustomDraw. Puoi fare i tuoi calcoli lì.

10

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

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

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

o prendendo 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; 
+0

Nel caso in cui si sottoclassi un TQuery o un altro tipo TDataset, non è necessario "hackerare" per accedere ai campi protetti. Questa risposta è il modo corretto per andare quando si (a) si aggiunge un campo calcolato nel codice e (b) l'intera cosa è nel codice, come se si sta scrivendo una query o un componente personalizzato interamente nel codice. –

1

Se si conoscono i nomi dei campi da calcolare al runti io, puoi usare qualcosa 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; 
Problemi correlati