将 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;
我认为此组件中应该改进的是(至少)备份原始提取选项集,并在批处理移动完成后和重新打开数据集之前进行后续恢复。
我有一个实用函数可以使用 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;
我认为此组件中应该改进的是(至少)备份原始提取选项集,并在批处理移动完成后和重新打开数据集之前进行后续恢复。