Delphi 10.3.3 FireDAC: DBGrid/FDQuery/MySQL VCL

Всем привет,

У меня есть таблица с этими полями

----------------------
|  id  |    data      |
----------------------
|  1   | 0=A;1=B;2=C  |
|  2   | 2=Z          |
|  3   |              |
|  4   | 0=Y;1=X      |
|  5   |              |
|  6   |              |

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

Я хотел бы, чтобы это отображалось в DBGRID:

-----------------------
|  id  | C0 | C1 | C2 |
-----------------------
|  1   | A  | B  | C  |
|  2   | A  | B  | Z  |
|  3   | A  | B  | Z  |
|  4   | Y  | X  | Z  |
|  5   | Y  | X  | Z  |
|  6   | Y  | X  | Z  |

На данный момент я могу сделать только следующую таблицу:

-----------------------
|  id  | C0 | C1 | C2 |
-----------------------
|  1   | A  | B  | C  |
|  2   |    |    | Z  |
|  3   |    |    |    |
|  4   | Y  | X  |    |
|  5   |    |    |    |
|  6   |    |    |    |

Для получения этого результата я создаю дополнительные столбцы в событии FDQuery1.BeforeOpen И в событии OnCreateFields я заполняю каждый столбец, но я не знаю содержимое предыдущей строки,

Итак, как мне заполнить недостающие поля в DBgrid? Спасибо, Франк.

0
franckcl 29 Апр 2020 в 13:38
Вместо этого вы можете использовать TStringGrid.
 – 
Olivier
29 Апр 2020 в 13:46
Всегда ли первая строка содержит все состояния? Вам нужно отредактировать или добавить строку?
 – 
sddk
29 Апр 2020 в 13:56
Оливье: я пробовал с TStringGrid, но обновление занимает больше времени (5 раз)
 – 
franckcl
29 Апр 2020 в 15:06
Sddk: первая строка не всегда содержит все состояния. Мне не нужно редактировать или добавлять строку, просто отобразите ее.
 – 
franckcl
29 Апр 2020 в 15:07
Что вы подразумеваете под "больше времени для обновления"? Отобразить сетку? Это не должно. Сколько у вас рядов?
 – 
Olivier
29 Апр 2020 в 15:32

1 ответ

Я думаю, вы имеете в виду OnCalcFields, а не OnCreateFields.

То, что вам нужно, безусловно, возможно, либо на стороне сервера, получая необходимые значения из предыдущей строки, используя, например. подзапрос SQL или на стороне клиента с использованием вычисляемых полей. Этот ответ о том, как сделать это на стороне клиента.

Проблема с выполнением расчетов на стороне клиента с использованием другой строки набора данных заключается в том, что для этого вам нужно иметь возможность перемещать курсор набора данных во время события OnCalcFields. Однако в это время набор данных будет находиться либо в состоянии dsCalcFields, либо в состоянии dsInternalCalc. и, пока это так, вы не можете легко перейти к другой строке в наборе данных. Возможно сделать это, но требует объявления класса набора данных-потомка (TMyFDQuery), чтобы вы могли получить доступ к SetTempState необходимо сделать возврат к предыдущему состоянию после того, как вы получили необходимую информацию от «другого» row и, если то, что вам нужно, включает более одного поля, вам нужно где-то временно хранить значения. Так что делать это таким образом становится грязно.

Гораздо более чистый подход предполагает использование функционального сходства между наборами данных FireDAC и наборами данных TClientDataSet. Одной из приятных особенностей TClientDatasSets является легкость, с которой вы можете перемещать содержимое набора данных между двумя CDS, просто выполняя

CDS2.Data := CDS1.Data;

Наборы данных FireDAC могут выполнять тот же трюк, но между любыми типами наборов данных FD. Итак, что бы я сделал в вашей ситуации:

  1. Добавьте FDMemTable в свою форму/модуль данных и скопируйте в него данные запроса в событии AfterOpen FDQuery следующим образом:
procedure TForm2.FDQuery1AfterOpen(DataSet: TDataSet);
begin
  FDQuery1.DisableControls;
  try
    FDMemTable1.Data := FDQuery1.Data;
    FDMemTable1.Open;
  finally
    FDQuery1.First;
    FDQuery1.EnableControls;
  end;
end;

FDQuery1.First должен заставить его повторно выполнить свои вычисляемые поля после того, как данные FDMemTable станут доступны (во время начального FDQuery1.Open этого, конечно, быть не может).

  1. В событии FDQuery OnCalcFields используйте подобный код, чтобы основывать значения вычисляемых полей на значениях, взятых из предыдущей строки (если, конечно, она есть, первая строка не может иметь «предыдущую» строку):
procedure TForm2.FDQuery1CalcFields(DataSet: TDataSet);
begin
  if FDMemTable1.Active then begin
    if FDMemTable1.Locate('ContactID', FDQuery1.FieldByName('ContactID').AsInteger, []) then begin
      FDMemTable1.Prior;
      if not FDMemTable1.Bof then begin
        //  Set FDQuery1's calculated fields that depend on prior row
        FDQuery1.FieldByName('PriorRowID').AsInteger := FDMemTable1.FieldByName('ContactID').AsInteger;
      end;
    end;
  end;
end;

В этом примере мой запрошенный набор данных имеет первичный ключ ContactID, а вычисленное значение — это просто значение ContactID из предыдущей строки. В жизни, конечно, так Было бы более эффективно использовать постоянные переменные поля, а не продолжать вызывать FieldByName.

Я полагаю, что другой возможностью может быть использование метода CloneCursor для получения курсора поиска для доступа к «предыдущей» строке, но я сам этого не пробовал, и в любом случае это может быть невозможно (что происходит с вычисляемыми полями в копии CloneCuror ?).

1
MartynA 29 Апр 2020 в 18:56
Большое спасибо за ваш очень полный ответ. Я провел некоторое тестирование с помощью FDmemTable, и это похоже на решение. Теперь мне нужно интегрировать это в мое приложение. Спасибо еще раз !
 – 
franckcl
29 Апр 2020 в 18:49
Рад помочь. Как только вы закончите его интеграцию, было бы здорово, если бы вы могли принять этот ответ (щелкнув значок галочки в его левой части).
 – 
MartynA
29 Апр 2020 в 18:58