Расчетный процент в Delphi DBGrid

Я поддерживаю приложение Delphi уже несколько дней. Заказчик хочет добавить процентный столбец в DBGrid, в котором теперь отображается столбец «Количество». Конечно, в процентах будет количество строк / общее количество * 100.

Я не могу изменить базовый TADOTable, поскольку он используется в другом месте кода, но я попытался добавить вычисляемое поле в TADOTable, но, похоже, вычисленное поле не может иметь значение, основанное на агрегированном значении (то есть сумме количества).

Мне удалось добавить пустой столбец в DBGrid, но есть ли способ заполнить значения% в каждой строке?


person Stefano Losi    schedule 18.01.2018    source источник
comment
Какой тип базы данных (MS Sql Server, MySql и т. Д.) Вы используете?   -  person MartynA    schedule 18.01.2018
comment
Я использую MS Access. Нет, к сожалению, хранимые процедуры невозможны ....   -  person Stefano Losi    schedule 18.01.2018
comment
Я думаю о переключении с сетки с привязкой к данным на сетку без привязки к данным, поскольку она предназначена только для визуализации данных - без редактирования   -  person Stefano Losi    schedule 18.01.2018
comment
Просто для справки, я закончил использование другого компонента сетки (TLMDGrid от LMD Innovative), которому не нужен базовый набор данных, а содержимое каждой ячейки можно адресовать с помощью синтаксиса thisGrid.Cells [col, row]: = value;   -  person Stefano Losi    schedule 27.01.2018


Ответы (2)


То, что вы хотите, сделать просто, но вы должны быть осторожны, как вы это делаете.

Оставив на время аспект графического интерфейса в стороне, вы хотите добавить вычисляемый столбец в свой TAdoDataSet и инициализировать его в событии OnCalcFields. Однако то, что вы НЕ хотите делать, - это вычислять TotalQuantity в этом случае. потому что а) событие OnCalcFields будет вызываться для каждой строки в наборе данных И б) выполнение чего-либо внутри события OnCalcFields, которое перемещает курсор набора данных - например, обход набора данных, как предложено в другом ответе, - будет рекурсивно вызывать событие OnCalcFields.

Способ избежать этой проблемы рекурсии и избежать выполнения какой-либо дополнительной работы, чем это необходимо, - это вычислять TotalQuantity только при первом открытии таблицы и в любое время, когда ее значение может измениться, то есть при редактировании, вставке или удалении строки. , а затем сохраните результат в поле вашей формы или модуля данных. Есть два основных способа сделать это вычисление: 1) использовать TAdoQuery для выполнения Sql, например «SELECT SUM (Quantity) FROM MyTable», или 2) использовать второй экземпляр TAdoDataSet, открытый в вашей таблице. Желательно, чтобы к этому второму экземпляру не было подключено никаких элементов управления графическим интерфейсом, чтобы его можно было перемещать как можно быстрее без использования DisableControls и EnableControls.

Чтобы добавить поле Percentage в свой набор AdoDataSet, дважды щелкните его, чтобы открыть редактор полей, щелкните его правой кнопкой мыши и выберите New field. Убедитесь, что вы установили Type на Calculated.

После настройки GetTotalQuantity процедуры вам необходимо настроить обработчики событий для ее вызова из событий BeforeOpen, BeforeInsert, BeforeEdit и AfterDelete вашего AdoDataSet.

Затем в событии OnCalcFields вычислите и присвойте ему вычисленное значение процента.

Код, необходимый для всего этого, довольно тривиален, что-то вроде

procedure TForm1.GetTotalQuantity;
begin
  //  AdoQuery1 contains Sql to obtain the sum of the AdoDataSet's
  if AdoQuery1.Active then
    AdoQuery1.Close;
  AdoQuery1.Open;
  try
    TotalQuantity := AdoQuery1.Fields[0].AsFloat;  //  TotalQuantity is a field of your for, (or datamodule)
  finally
    AdoQuery1.Close;
  end;
end;

Or

procedure TForm1.GetTotalQuantity;
begin
  //  Note: AdoDataSet2 is a second instance of TAdoDataSet set up to access the same
  //  db table as the one connected to the OP's DBGrid
  if AdoDataSet2.Active then
    AdoDataSet2.Close;
  AdoDataSet2.Open;
  try
    TotalQuantity := 0;
    while not AdoDataSet2.Eof do begin
      TotalQuantity :=  TotalQuantity + AdoDataSet2Quantity.AsFloat; // AdoDataSet2.Quantity.AsFloat;
      AdoDataSet2.Next;
    end;
  finally
    AdoDataSet2.Close;
  end;
end;

Событие OnCalcFields:

procedure TForm1.AdoDataSet1CalcFields(DataSet : TDataSet);
begin
  if TotalQuantity > 0 then
    AdoDataSet1Percentage.AsFloat := AdoDataSet1Quantity.AsFloat / Total Quantity * 100;
end;

После того, как вы добавили вычисляемое поле Percentage в свой AdoDataSet и настроили событие OnCalcFields для набора данных, ваша DBGrid с радостью отобразит его, как и любое другое поле набора данных.

person MartynA    schedule 18.01.2018
comment
Ой! Я внес небольшую поправку во вторую версию GetTotalQuantity. - person MartynA; 18.01.2018
comment
Я заметил, что вы отказались от своего согласия и проголосовали за мой ответ. У вас были с этим проблемы? - person MartynA; 19.01.2018

Вы пробовали рассчитать процент в событии OnCalcFields? Не уверен на 100% в следующем, но этот пример может вам помочь:

procedure TClass.DataSetCalcFields(DataSet: TDataSet);
var
  Bookmark: String;
  TotalQuantity: Double;
begin
    // Save current position
    Bookmark := Dataset.Bookmark;

    // Calculate the total quantity in a while loop through the dataset:
    Dataset.First;
    while not Dataset.Eof do
    begin
      TotalQuantity := TotalQuantity + Dataset.FieldByName('QUANTITY').AsFloat;
      Dataset.Next;
    end;

    // Load current position
    Dataset.Bookmark := Bookmark;

    // Calculate the percentage:
    if TotalQuantity > 0 then
    begin
      Dataset.FieldByName('PERCENTAGE').AsFloat := Dataset.FieldByName('QUANTITY').AsFloat / TotalQuantity * 100;
    end
    else
    begin
      Dataset.FieldByName('PERCENTAGE').AsFloat := 0;
    end;
  end;
end;
person NilsBremer    schedule 18.01.2018
comment
Кроме того, учитывали ли вы влияние этого способа на производительность? - person MartynA; 18.01.2018
comment
@MartynA Да, это наверняка проблема, но это было самое быстрое решение, которое пришло мне в голову ... определенно есть лучший способ получить общее количество. Я просто хотел показать OP пример события OnCalcFields. - person NilsBremer; 18.01.2018