我正在使用 Delphi 中的查询获取数据,并且希望在运行之前向查询添加计算字段。计算字段使用代码和查询中的值,因此我不能只在 SQL 中计算它。

我知道我可以附上 OnCalcFields 实际进行计算的事件,但问题是添加计算字段后查询中没有其他字段...

我做了一些挖掘,发现所有字段定义都已创建,但实际字段仅创建

if DefaultFields then
    CreateFields

指定默认字段

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

这表明如果您添加字段,您只会获得您添加的字段。

我想要查询中的所有字段以及我添加的字段。

这是可能的还是我必须添加我正在使用的所有字段?

有帮助吗?

解决方案

Delphi 现在可以选择组合自动生成的字段和计算的字段: Data.DB.TFieldOptions.AutoCreateMode 类型的枚举 TFields自动创建模式. 。这样您就可以在运行时添加计算字段。弗朗索瓦在他的回答中写道如何在运行时添加字段。

TFieldsAutoCreationMode 的不同模式:

  • ac独家

    当根本没有持久字段时,就会创建自动字段。这是默认模式。

  • acCombine计算

    当数据集没有持久字段或仅存在计算持久字段时,将创建自动字段。这是在设计时创建持久计算字段并让数据集创建自动数据字段的便捷方法。

  • 始终合并

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

其他提示

没有什么可以阻止您首先在代码中创建所有字段,
然后添加您的计算字段。

您可以使用“黑客类型”来使用受保护的 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;

除了计算字段之外,您还需要添加所有字段。

添加字段后,您必须添加数据集中所需的所有字段。

Delphi 将其称为持久字段与动态字段。所有字段都是持久的或动态的。不幸的是,你不能混合两者。

另一件需要注意的事情是,从文档中可以看出

持久字段组件列表包括 存储在您的应用程序中,并且不要 即使改变结构 数据集的基础数据库是 改变。

因此,请小心,如果您稍后向表中添加其他字段,则需要将新字段添加到组件中。删除字段也是如此。

如果您确实不想要持久字段,还有另一种解决方案。在任何应显示计算字段的网格或控件上,您可以自定义绘制它。例如,许多网格控件都有 OnCustomDraw 事件。您可以在那里进行计算。

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

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;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top