Pergunta

Estou recebendo dados usando uma consulta em Delphi e gostaria de adicionar um campo calculado à consulta antes de executar. O campo calculado está usando valores no código e na consulta, para que eu não possa simplesmente calculá -lo no SQL.

Eu sei que posso anexar um OnCalcFields Evento para realmente fazer o cálculo, mas o problema é depois de adicionar o campo calculado, não há outros campos na consulta ...

Eu fiz algumas escavações e descobri que todos os defs de campo são criados, mas os campos reais são criados apenas

if DefaultFields then
    CreateFields

Campos padrão são especificados

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

O que indicaria que, se você adicionar campos, você só obtém os campos que adicionou.

Eu gostaria de todos os campos da consulta, bem como os que eu acrescento.

Isso é possível ou tenho que adicionar todos os campos que estou usando também?

Foi útil?

Solução

A Delphi agora tem a opção de combinar campos gerados automáticos e campos calculados: Data.db.tfieldOptions.autocreatemode Uma enumeração do tipo TfieldsAutocrationMode. Dessa forma, você pode adicionar seus campos calculados em tempo de execução. François escreveu em sua resposta como adicionar um campo em tempo de execução.

Diferentes modos de tfieldsautocrationMode:

  • Acexclusivo

    Quando não há campos persistentes, os campos automáticos são criados. Este é o modo padrão.

  • acumpinecomputado

    Os campos automáticos são criados quando o conjunto de dados não tem campos persistentes ou existem apenas campos persistentes calculados. Essa é uma maneira conveniente de criar os campos calculados persistentes no momento do design e permitir que o conjunto de dados crie campos de dados automáticos.

  • acumulações acumuladas

    Os campos automáticos para os campos do banco de dados serão criados quando não houver campos persistentes.

Outras dicas

Nada o impede de criar todos os campos primeiro em seu código,
Em seguida, adicione seus campos calculados.

Você pode usar um "tipo hackeado" para usar os createfields protegidos:

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

ou emprestando algum código 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]);

Em seguida, crie seus campos calculados:

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

Você precisa adicionar todos os campos, além do seu campo calculado.

Depois de adicionar um campo, você deve adicionar todos os campos que deseja no conjunto de dados.

Delphi chama esses campos persistentes versus campos dinâmicos. Todos os campos são persistentes ou dinâmicos. Infelizmente, você não pode ter uma mistura de ambos.

Outra coisa a observar, da documentação é

As listas de componentes de campos persistentes são armazenadas em seu aplicativo e não alteram, mesmo que a estrutura de um banco de dados subjacente a um conjunto de dados seja alterada.

Portanto, tenha cuidado, se você adicionar mais tarde campos adicionais a uma tabela, precisará adicionar os novos campos ao componente. A mesma coisa com os campos de exclusão.

Se você realmente não quer campos persistentes, há outra solução. Em qualquer grade ou controle que deve mostrar o campo calculado, você pode desenhá -lo personalizado. Por exemplo, muitos controles de grade têm um evento OnCustomDraw. Você pode fazer seu cálculo lá.

Se você sabe que o seu nome de campos calculado em tempo de execução, pode usar algo assim.

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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top