Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Calculated поля, которые зависят от суммы 1-го из


Автор: Delphist 12.9.2008, 10:36
Возникла довольно неприятная ситуации, сейчас на простом примере постораюсь объяснить:

Есть грид-DBGridEh, с 3-мя колонками:
одна из них связана с полем fkData компонента FIBDataSet, а две других с полями fkCalculated этого же датасета.

Код

|---колонка№1---|---колонка№2---|----колонка№3--| - колонки грида DBGridEh
|-------fkData-----|---fkCalculated---|----fkCalculated--| - привязка с полями FIBDataSet'a
|-------fvtSum-----|-------fvtSum------|------fvtSum------|- Footer DBGridEh'a,в нем отображается сумма значений записей нашего FIBDataSet'a


В обработчике FIBDataSet.OnCalcFields весит код который для каждой записи колонки №2 и №3 делает расчет типа:

Код

  DataSet.FieldByName('Field_2').AsFloat :=  GetSumFirstField * FuncX();
  DataSet.FieldByName('Field_3').AsFloat :=  GetSumFirstField * FuncY();

//GetSumFirstField - возращает сумму значений записией первой колонки (поля)
//FuncX, FuncY произвольные ф-ции


Возник большой вопрос какой должен быть код в GetSumFirstField, 
варианты которые я делал (для упрощения объема кода будут приведены прототипы):

Способ 1:
Код

//работает не всегда коректно например на переоткрытии DataSet'a
function GetSumFirstField: double;
begin
   Result := DBGridEh.GetColumnByFieldName('Field_1').Footer.SumValue;
end;


Способ 2:
Код

{в итоге работает медленно особенно, если БД не локальная, + ко всему возращает неправильный результат (старую сумму) если я
редактирую значение записи первой колонки}
function GetSumFirstField: double;
begin
   Result := ExecFibSql('select sum(field_1) as sum_f1 from MyTable').Query.FieldByName[sum_f1].AsFloat;
end;


Способ 3:
Код

{обмирает почему-то, не коректно возвращает сумму, + bookmark не работает - пробовал сохранить текущ. строку по ID и затем 
восстанавливать ее через Locate, но почему то добавляется лишняя в мой DataSet лишняя пустая строка, так и не понял откуда она 
берется :dash1 }
function GetSumFirstField: double;
begin
var
  context: TContextDS;
  fSource: TField;
  bm: TBookmark
begin

  Result := 0;  
  FastDS(DataSet, context); //блокируем AfterPost := nil; BeforePost := nil; OnCalcFields := nil
  if DataSet is TFIBDataSet then
    TFIBDataSet(DataSet).DisableScrollEvents;
  DataSet.DisableControls;
  bm := DataSet.GetBookmark; //возращает почему-то bm = nil из-за чего позиция текущей записи в finally не востанавливается 
  try
    fSource := DataSet.FieldByName('field_1');
    DataSet.First;
    while not DataSet.Eof do
    begin
        Result := Result + fSource.AsFloat;
      DataSet.Next;
    end;
  finally
    if DataSet.BookmarkValid(bm) then //DataSet.BookmarkValid(bm) = False, из-за bm=nil
    begin
      DataSet.GotoBookmark(bm);
      DataSet.FreeBookmark(bm);
    end;

    if DataSet is TFIBDataSet then
      TFIBDataSet(DataSet).EnableScrollEvents;
    DataSet.EnableControls;
    NormalDS(context); //восстанавливаем AfterPost := oldAfterPost, BeforePost := oldBeforePost, OnCalcFields := oldOnCalcFields
  end;
end;


Подскажите, пожалуйста, какой должен быть кода в GetSumFirstField, чтобы он всегда возвращал правильно сумму?

Автор: Delphist 15.9.2008, 09:04
Большая просьба помочь с ответом

Автор: Frees 15.9.2008, 09:51
Цитата(Delphist @  12.9.2008,  12:36 Найти цитируемый пост)
{в итоге работает медленно особенно, если БД не локальная, + ко всему возращает неправильный результат (старую сумму) если яредактирую значение записи первой колонки}
Код

function GetSumFirstField: double;begin   Result := ExecFibSql('select sum(field_1) as sum_f1 from MyTable').Query.FieldByName[sum_f1].AsFloat;end;


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

Автор: Frees 15.9.2008, 10:56
Цитата(Delphist @  12.9.2008,  12:36 Найти цитируемый пост)
 Способ 1: работает не всегда коректно например на переоткрытии DataSet'a


а в чем проявляется не корректная раббота?

может так будет работать лучше
Код

procedure TForm2.pFIBDataSet1AfterPost(DataSet: TDataSet);
begin
  pFIBDataSet1.RefreshClientFields();
end;

procedure TForm2.pFIBDataSet1AfterOpen(DataSet: TDataSet);
begin
  pFIBDataSet1.EnableCalcFields;
end;

procedure TForm2.pFIBDataSet1BeforeOpen(DataSet: TDataSet);
begin
  pFIBDataSet1.DisableCalcFields;
end;

procedure TForm2.pFIBDataSet1CalcFields(DataSet: TDataSet);
begin
   DataSet.FieldByName('Field_2').AsVariant := DBGridEh.GetColumnByFieldName('Field_1').Footer.SumValue;
end;

Автор: Delphist 16.9.2008, 09:10
Цитата(Frees @  15.9.2008,  10:51 Найти цитируемый пост)
работате медленно потому что запрос для каждой записи выполняется, ты его выполняй перед расчетом полей и ложи результат в переменную и при расчете полей использкуй переменную а не вызов  функций

Не очень хороший подход мне, кажется. На мой взгляд, не всегда, удастся определить именно цикл расчета для всех записей. Т.е., конечно, мы можем написать в процедуре типа BeforeFieldsCalc определение суммы, но и этот же BeforeFieldsCalc будет вызываться для каждой записи, а вот как определить, то место или процедуру которая бы вызывалась бы до расчета всех полей. Вот в это проблемно.

Автор: Delphist 16.9.2008, 10:07
Цитата(Frees @  15.9.2008,  11:56 Найти цитируемый пост)
а в чем проявляется не корректная раббота?
может так будет работать лучше

К сожалению, на сегодняшний день мне приходиться пользоваться компонентами FIBs, это не много урезанный вариант FIBPlus.  В них  нет RefreshClientFields(), DisableCalcFields, EnableCalcFields. 
Вот это:
Код

DisableCalcFields, 
EnableCalcFields.

в принцепе их можно реализовать, на скок я понимаю, через 
Код

 //DisableCalcFields
 oldCF := OnCalcFields; 
 OnCalcFields := nil;

//EnableCalcFields.
OnCalcFields := oldCF;


RefreshClientFields() - не знаю что делает smile, подозреваю что выполняет что-то типа RecalcAllFields - пересчитать все поля

И опять же у меня берет сомнение, что AfterPost, будет всегда срабатывать после AfterOpen smile 

Автор: Frees 16.9.2008, 10:31
Цитата(Delphist @  16.9.2008,  12:07 Найти цитируемый пост)
RefreshClientFields() - не знаю что делает

пересчет вычисляемых полей запускается


Автор: Delphist 16.9.2008, 10:43
Цитата(Frees @  16.9.2008,  11:31 Найти цитируемый пост)
пересчет вычисляемых полей запускается

угу уже понял, посмотри, пожалуйста, еще раз мой последний ответ, в особеннности последнее предложение

Автор: Frees 16.9.2008, 11:00
Цитата(Delphist @  16.9.2008,  12:07 Найти цитируемый пост)
И опять же у меня берет сомнение, что AfterPost, будет всегда срабатывать после AfterOpen 

точно будет сперва AfterOpen на закрытом дата сете же пост не сделать

Автор: Delphist 16.9.2008, 11:30
Цитата(Frees @  16.9.2008,  12:00 Найти цитируемый пост)
точно будет сперва AfterOpen на закрытом дата сете же пост не сделать 

не совсем тебя понял. ты согласился с моими выводом или наоборот утверждаешь, что AfterPost будет всегда после AfterOpen'a?

Автор: Frees 16.9.2008, 11:32
 AfterPost будет всегда после AfterOpen'a

Автор: Delphist 16.9.2008, 13:06
Цитата(Frees @  16.9.2008,  12:32 Найти цитируемый пост)
AfterPost будет всегда после AfterOpen'a 

Ток что проверил, и делаю вывод AfterPost НЕ вызывается после AfterOpen и это правильно как по сути так и по логике.

Автор: Frees 16.9.2008, 13:26
Цитата(Delphist @  16.9.2008,  15:06 Найти цитируемый пост)
Ток что проверил, и делаю вывод AfterPost НЕ вызывается после AfterOpen и это правильно как по сути так и по логике.

AfterPost будет происходить если ты будеш делать post 
но до этого уже отработается AfterOpen

отработать AfterPost нужно что бы если ты вносиш изменения перещитались все поля без переоткрытия

Автор: Delphist 16.9.2008, 14:11
Цитата(Frees @  16.9.2008,  14:26 Найти цитируемый пост)
AfterPost будет происходить если ты будеш делать post 

Ты предлагаешь искусствено вызывать Post после AfterOpen? Опять же Post можно вызывать ток в том случае если DataSet.State in [dsEdit, dsInsert] а иначе генириться исключение.

Автор: Frees 16.9.2008, 14:20
Цитата(Delphist @  16.9.2008,  16:11 Найти цитируемый пост)
Ты предлагаешь искусствено вызывать Post после AfterOpen? Опять же Post можно вызывать ток в том случае если DataSet.State in [dsEdit, dsInsert] а иначе генириться исключение.

ты меня не понял в AfterPost можно ничего не писать и все будет работать только до того момента пока ты не начнеш редактировать первое поле, а что бы учесть его редактирование на AfterPost и делаем перещет полей

Автор: Delphist 19.9.2008, 10:10
Цитата(Frees @  16.9.2008,  15:20 Найти цитируемый пост)
ты меня не понял в AfterPost можно ничего не писать и все будет работать только до того момента пока ты не начнеш редактировать первое поле, а что бы учесть его редактирование на AfterPost и делаем перещет полей 

Что-то я  так и не понял так какой же код должен быть в GetSumFirstField. Опять же твоя методика при AfterOpen вернет не правильную сумму.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)