将 FireDac BatchMove 与查询中的活动索引结合使用

Using FireDac BatchMove with an Active Index on a Query

我有一个实用函数可以使用 TFDBatchMove 对象将 FireDac 查询导出到 csv 文件。

我当前的数据集有一个索引,我希望在调用 ExportToCsv 函数时按索引顺序导出数据。但是,设置了 .IndexName 后,我收到错误消息:EFDException with message '[FireDAC][Comp][DS]-211。无法对单向数据集 [FDQuery1]' 执行操作。

我仔细检查了获取选项,更改 CursorKind 没有任何区别。我使用静态游标。

我已经追踪到 FireDac 代码,问题似乎是在 BatchMove.Execute 结束时重新打开查询时发生的。我已经检查过,我的输出文件确实包含所有数据。

TFDBatchMove.Execute 调用 Reader.Refresh 似乎关闭了查询。然后它调用 Reader.Close(False),尽管它的名称设置 Dataset.Active := true 试图重新打开查询。将 Active 设置为 true 会引发错误。

下面是一些示例代码,我调用 ExportToCsv 时没有错误,设置索引名称,然后第二次调用 ExportToCsv 导致错误。

有办法解决这个问题吗?为什么 FireDac 关闭并重新打开查询?为什么在索引处于活动状态时无法重新打开查询?

procedure TDemoData.ExportToCsvTest();
begin
  FDQuery1.Connection := MyDataAccess.Connection;
  FDQuery1.FetchOptions.CursorKind := ckStatic;

  FDQuery1.Sql.Text := 'Select PeriodPost, DebitAmt from Trans';
  FDQuery1.Open;

  with FDQuery1.Indexes.Add do
  begin
    Name := 'TestIndex';
    Fields := 'PeriodPost';
    Options := [];
    Active := true;
    Selected := false; // Start out with no current index
  end;

  // This export works fine.
  ExportToCsv(FDQuery1, 'c:\localdata\test1.csv');

  // After setting an Active IndexName there will be an exception:
  // EFDException with message '[FireDAC][Comp][DS]-211.
  // Cannot perform operation on unidirectional dataset [FDQuery1]'.
  FDQuery1.IndexName := 'TestIndex';
  ExportToCsv(FDQuery1, 'c:\localdata\test2.csv');
end;


procedure ExportToCsv(DataSet: TFDDataSet; FileName: string);
var
  TextWriter: TFDBatchMoveTextWriter;
  DataReader: TFDBatchMoveDataSetReader;
  BatchMove: TFDBatchMove;
begin

  DataReader := nil;
  TextWriter := nil;
  BatchMove := nil;

  try
    DataReader := TFDBatchMoveDataSetReader.Create(nil);
    TextWriter := TFDBatchMoveTextWriter.Create(nil);
    BatchMove := TFDBatchMove.Create(nil);

    DataReader.DataSet := DataSet;
    DataReader.Rewind := true;

    TextWriter.FileName := FileName;
    TextWriter.DataDef.WithFieldNames := true;
    TextWriter.DataDef.Separator := ',';
    BatchMove.Options := [poClearDestNoUndo, poCreateDest];

    BatchMove.Reader := DataReader;
    BatchMove.Writer := TextWriter;
    BatchMove.Execute;

  finally
    DataReader.Free;
    TextWriter.Free;
    BatchMove.Free;
  end;

end;

就我个人而言,我不理解为什么 FireDAC 在批量移动完成后重新打开数据集对象。但是,您的问题是由 Optimise option enabled. With this option enabled, the engine modifies dataset option set for speed which includes enabling Unidirectional 选项引起的,正如您现在可能猜到的那样,该选项与自定义索引规范相互排斥。所以,我们实际上可以将问题缩小为这样的代码:

FDQuery1.FetchOptions.Unidirectional := True;
FDQuery1.IndexName := 'MyIndex';
FDQuery1.Close;
FDQuery1.Open;

正如我所说,我不知道为什么 FireDAC 需要重新打开只被读取的源数据集,因此我只是建议关闭 Optimise 选项(但你可以编写自己的 reader):

DataReader.Optimise := False;

我认为此组件中应该改进的是(至少)备份原始提取选项集,并在批处理移动完成后和重新打开数据集之前进行后续恢复。