2010-03-26 27 views
7

Ich erhalte Daten mithilfe einer Abfrage in Delphi und möchte der Abfrage ein berechnetes Feld hinzufügen, bevor es ausgeführt wird. Das berechnete Feld verwendet Werte in Code sowie die Abfrage, so dass ich es nicht einfach in SQL berechnen kann.Hinzufügen eines berechneten Felds zu einer Abfrage zur Laufzeit

Ich weiß, dass ich ein OnCalcFields Ereignis anhängen, um tatsächlich die Berechnung zu machen, aber das Problem ist, nachdem das berechnete Feld Hinzufügen es keine anderen Felder in der Abfrage sind ...

ich einige graben tat und fand, dass alle das Feld defs erstellt werden, sondern die tatsächlichen Felder sind

nur
if DefaultFields then 
    CreateFields 

Standardfelder erstellt werden

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

Whi angegeben Wenn Sie Felder hinzufügen, erhalten Sie nur die Felder, die Sie hinzugefügt haben.

Ich möchte alle Felder in der Abfrage so gut wie diejenigen, die ich hinzufügen.

Ist das möglich oder muss ich alle Felder, die ich verwende, hinzufügen?

+0

Ich verstehe nicht, warum Sie nicht die Werte aus dem Code in SQL verwenden können .... ich bauen dynamische SQL-Anweisungen regelmäßig, dass die Verwendung Werte aus dem Code in der SQL .... – Leslie

+0

sehen meine antwort für einen neuen weg dies zu tun seit delphi berlin –

+0

hast du versucht, die frage vorzubereiten? Es könnte fielddefs (noch nicht Feldobjekte) –

Antwort

-1

Delphi hat nun die Möglichkeit, automatisch generierte Felder zu kombinieren und berechnete Felder: Data.DB.TFieldOptions.AutoCreateMode eine Aufzählung von Typ TFieldsAutoCreationMode. Auf diese Weise können Sie Ihre berechneten Felder zur Laufzeit hinzufügen. Francois schrieb in seiner Antwort, wie man zur Laufzeit ein Feld hinzufügt.

Verschiedene Modi von TFieldsAutoCreationMode:

  • acExclusive

    Wenn es keine persistenten Felder überhaupt, dann automatische Felder erstellt werden. Dies ist der Standardmodus.

  • acCombineComputed

    Die automatischen Felder erstellt werden, wenn der Datensatz keine persistenten Felder hat oder es nur berechnet persistente Felder. Auf diese Weise können Sie die persistent berechneten Felder zur Entwurfszeit erstellen und das Datenset automatische Datenfelder erstellen lassen.

  • acCombineAlways

    Automatische Felder für die Datenbankfelder erstellt werden, wenn es keine persistenten Felder sind.

+0

Es sieht so aus, als ob hiermit berechnete Felder für die Entwurfszeit und Datenfelder für die Laufzeit kombiniert werden können. Haben Sie das, was Sie in der Antwort behaupten, erfolgreich getestet? –

+0

Das ist keine Mathe-Hausaufgabe, wo man alles "beweisen" muss. Der Code zum Erstellen eines Felds zur Laufzeit wird in einer anderen Antwort beschrieben –

+0

Nun, die Dokumentation sagt nicht, was Sie behaupten. –

3

Sie müssen alle Felder zusätzlich zu Ihrem berechneten Feld hinzufügen.

Sobald Sie ein Feld hinzufügen, müssen Sie alle gewünschten Felder im Datensatz hinzufügen.

Delphi nennt diese persistenten Felder im Vergleich zu dynamischen Feldern. Alle Felder sind entweder persistent oder dynamisch. Leider können Sie keine Mischung aus beidem haben.

andere Sache zu beachten, von der Dokumentation

Persistent Felder Komponentenlisten in Ihrer Anwendung gespeichert sind ist, und zwar nicht ändern, auch wenn die Struktur einer Datenbank einen Datensatz ist geändert zugrunde liegen.

Seien Sie also vorsichtig, wenn Sie später weitere Felder zu einer Tabelle hinzufügen, müssen Sie der Komponente die neuen Felder hinzufügen. Das gleiche gilt für das Löschen von Feldern.

Wenn Sie wirklich persistente Felder nicht wollen, gibt es eine andere Lösung. Auf jedem Raster oder Steuerelement, das das berechnete Feld anzeigen soll, können Sie es benutzerdefiniert zeichnen. Beispielsweise haben viele Grid-Steuerelemente ein OnCustomDraw-Ereignis. Sie können dort Ihre Berechnung durchführen.

10

Nichts hindert Sie daran, alle Felder zuerst in Ihrem Code zu erstellen,
dann fügen Sie Ihre berechneten Felder hinzu.

Sie können entweder eine "gehackt Typ" verwenden, um die geschützten Create zu verwenden:

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

oder einen Code von Create borgen:

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]); 

dann berechnete Felder erstellen:

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

erstellt haben Wenn Sie einen TQuery oder einen anderen TDataset-Typ bilden, müssen Sie nicht "hacken", um auf geschützte Felder zuzugreifen. Diese Antwort ist der richtige Weg, wenn Sie (a) ein berechnetes Feld im Code hinzufügen und (b) die gesamte Sache im Code ist, so wie Sie eine benutzerdefinierte Abfrage oder Komponente vollständig im Code schreiben. –

1

Wenn Sie wissen, Ihre Felder Namen bei RUNTI berechnet werden ich, du kannst so etwas benutzen.

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; 
Verwandte Themen