delphi 在运行时向查询添加计算字段

brjng4g3  于 2023-03-12  发布在  其他
关注(0)|答案(5)|浏览(255)

我正在 Delphi 中使用查询获取数据,并希望在查询运行前向其添加计算字段。计算字段使用代码和查询中的值,因此我无法仅在SQL中计算它。
我知道我可以附加一个OnCalcFields事件来实际进行计算,但问题是在添加计算字段之后,查询中就没有其他字段了...
我做了一些调查,发现所有字段def都已创建,但实际字段仅创建

if DefaultFields then
    CreateFields

已指定默认字段

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

这表明如果添加字段,则只会得到添加的字段。
我想要查询中的所有字段以及我添加的字段。
这是可能的,还是我必须添加我正在使用的所有字段?

mlmc2os5

mlmc2os51#

**没有什么可以阻止您首先在代码中创建所有字段,

然后添加计算字段。**
您可以使用“黑客类型”来使用受保护的CreateFields:

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

或者从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]);

然后创建计算字段:

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

wtzytmuj2#

Delphi 现在可以选择合并自动生成的字段和计算字段:Data.DB.TFieldOptions.AutoCreateModeTFieldsAutoCreationMode类型的枚举。这样你就可以在运行时添加你的计算字段。Francois在他的答案中写了如何在运行时添加字段。
T字段自动创建模式的不同模式:

*acExclusive

如果根本没有持久化字段,则创建自动字段。这是默认模式。

*ac组合计算

当数据集没有持久性字段或只有计算持久性字段时,将创建自动字段。这是一种在设计时创建持久性计算字段并让数据集创建自动数据字段的简便方法。

*ac始终组合

当没有永久字段时,将为数据库字段创建自动字段。

fnx2tebb

fnx2tebb3#

您需要添加除计算字段之外的所有字段。
添加字段后,必须在数据集中添加所需的所有字段。
Delphi 称之为持久化字段和动态字段,所有的字段要么是持久化的,要么是动态的,不幸的是,你不能两者兼得。
文档中另一件需要注意的事情是
持久性字段组件列表存储在应用程序中,即使数据集的基础数据库结构发生更改,它们也不会更改。
因此,要小心,如果您以后向表中添加额外的字段,您需要将新字段添加到组件中。删除字段也是如此。
如果您确实不需要永久性字段,还有另一个解决方案。在任何应显示计算字段的网格或控件上,您可以自定义绘制它。例如,许多网格控件都有OnCustomDraw事件。您可以在那里进行计算。

6qqygrtg

6qqygrtg4#

如果你在运行时知道你要计算的字段名,你可以使用类似的东西。

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;
jecbmhm3

jecbmhm35#

//assume we have an fdquery named  FDQuery1
    //we write this code in AfterOpen section

注意:这个方法有一个问题计算字段被放在最后一个字段的位置要解决这个问题,在查询中添加一个额外的字段就足够了,例如,考虑值零作为最后一个字段,如下所示:

select * , 0 as myfield from mytable

procedure Tform1.FDQuery1AfterOpen(DataSet: TDataSet);
var
 i : Integer;
 aField : TField;
begin
    FDQuery1.Close;
    for i := 0 to FDQuery1.FieldDefs.Count - 1 do begin
     aField := FDQuery1.FieldDefs [ i ].CreateField ( fdQuery1 );
    end;
    aField.DataSet := fdQuery1;
    with aField do
    begin
      Name := 'myField';
      FieldKind := fkCalculated;
      FieldName := 'myField';
      Size := 10;
      DataSet := FDQuery1;
    end;
    fdQuery1.FieldDefs.Update;
    fdQuery1.AfterOpen:=nil;
    fdQuery1.Open;
    ShowMessage(FDQuery1.FieldByname('myField').AsString);
end;

现在可以为myField编写onClacField方法

相关问题