【问题标题】:Adding a calculated field to a Query at run time在运行时将计算字段添加到查询
【发布时间】:2011-02-01 04:41:03
【问题描述】:

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

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

我做了一些挖掘,发现所有的字段定义都被创建了,但实际的字段只是被创建了

if DefaultFields then
    CreateFields

指定默认字段

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

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

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

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

【问题讨论】:

  • 我不明白为什么你不能在你的 SQL 中使用代码中的值......我定期构建动态 SQL 语句,这些语句使用 SQL 中代码中的值......
  • 查看我的答案,了解自 delphi Berlin 以来的新方法
  • 您是否尝试准备查询?它可能已经创建了 fielddefs(虽然还不是字段对象)

标签: delphi calculated-field


【解决方案1】:

Delphi 现在可以选择组合自动生成的字段和计算的字段:Data.DB.TFieldOptions.AutoCreateMode 类型为 TFieldsAutoCreationMode 的枚举。这样,您可以在运行时添加计算字段。 Francois 在他的回答中写道如何在运行时添加字段。

TFieldsAutoCreationMode的不同模式:

  • ac 独家

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

  • acCombineComputed

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

  • acCombineAlways

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

【讨论】:

  • 看起来这允许组合设计时计算字段和运行时数据字段。您是否测试/成功了您在答案中声称的内容?
  • 这不是数学作业,你必须“证明”一切。在另一个答案中描述了在运行时创建字段的代码
  • 好吧,文档并没有说明您声称的内容。
  • 在 C++ Builder XE8 中测试,一切正常。这比 Francois 的解决方案更简单,因为您不必在项目中添加额外的代码。
【解决方案2】:

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

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;

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 在子类化 TQuery 或其他 TDataset 类型的情况下,您不需要“破解”来访问受保护的字段。当您 (a) 在代码中添加计算字段,并且 (b) 整个事情都在代码中,例如您完全在代码中编写自定义查询或组件时,这个答案是正确的方法。
    【解决方案4】:

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

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

    Delphi 将此称为持久字段与动态字段。所有字段要么是持久的,要么是动态的。不幸的是,你不能同时拥有两者。

    另一件需要注意的事情,来自文档

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

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

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

    【讨论】:

      猜你喜欢
      • 2021-04-01
      • 2017-02-07
      • 2021-04-30
      • 2016-11-01
      • 1970-01-01
      • 2017-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多