Добавление вычисляемого поля в запрос во время выполнения

StackOverflow https://stackoverflow.com/questions/2523697

  •  22-09-2019
  •  | 
  •  

Вопрос

Я получаю данные с помощью запроса в Delphi и хотел бы добавить вычисляемое поле в запрос перед его запуском.Вычисляемое поле использует значения как в коде, так и в запросе, поэтому я не могу просто вычислить его в SQL.

Я знаю, что могу прикрепить OnCalcFields Событие для фактического выполнения расчета, но проблема в том, что после добавления вычисляемого поля в запросе нет других полей...

Я немного покопался и обнаружил, что все определения полей создаются, но фактические поля создаются только

if DefaultFields then
    CreateFields

Поля по умолчанию указаны

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

Это означает, что если вы добавите поля, вы получите только те поля, которые вы добавили.

Мне нужны все поля в запросе, ТАКЖЕ, КАК те, которые я добавляю.

Возможно ли это, или мне нужно добавить все поля, которые я использую?

Это было полезно?

Решение

В Delphi теперь есть возможность комбинировать автоматически сгенерированные и вычисляемые поля: Data.DB.TFieldOptions.AutoCreateMode перечисление типа TFieldsAutoCreationMode.Таким образом, вы можете добавлять вычисляемые поля во время выполнения.Франсуа написал в своем ответе, как добавить поле во время выполнения.

Различные режимы TFieldsAutoCreationMode:

  • acExclusive

    Когда постоянных полей вообще нет, создаются автоматические поля.Это режим "по умолчанию".

  • acCombineComputed

    Автоматические поля создаются, когда в наборе данных нет постоянных полей или есть только вычисляемые постоянные поля.Это удобный способ создать постоянные вычисляемые поля во время разработки и позволить набору данных создавать автоматические поля данных.

  • acCombineAlways

    Автоматические поля для полей базы данных будут созданы, если постоянных полей нет.

Другие советы

Ничто не мешает вам сначала создать все поля в вашем коде,
затем добавьте вычисляемые поля.

Вы можете использовать «взломанный тип» для использования защищенных 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