FireDAC 查询 RecordCountMode

FireDAC Query RecordCountMode

我正在尝试配置一个 FireDAC TFDQuery 组件,以便它按需以不超过 500 条的批次获取记录,但我需要它来报告查询的总记录数,而不仅仅是获取的记录数记录。 FetchOptions配置如下:

FetchOptions.AssignedValues = [evMode, evRowsetSize, evRecordCountMode, evCursorKind, evAutoFetchAll]
FetchOptions.CursorKind = ckForwardOnly
FetchOptions.AutoFetchAll = afTruncate
FetchOptions.RecordCountMode = cmTotal
FetchOptions.RowSetSize = 500

这立即 returns table 中的所有记录,而不仅仅是 500。我尝试将 RecsMax 设置为 500,这可以限制获取的记录,但是 RecordCount查询只显示500不是总数。

FireDAC 帮助文件指出将 RecordCountMode 设置为“cmTotal”会导致 FireDAC 发出

SELECT COUNT(*) FROM (original SQL command text).

不是有bug就是我做错了什么!

我看不到我可以更改哪些其他属性。我对 RowSetSizeRecsMax 之间的关系感到困惑,并且没有找到澄清的帮助文件。

我尝试使用 AutoFetchAll 的属性(再次对此属性的用途感到困惑),但注意到它被设置为 afAll 我将其设置为 afTruncate看看这是否会有所作为,但事实并非如此。

我已经用 FDTable 组件和 FDQuery 组件测试了 FetchOptions' fmOnDemand Mode。两者都具有相同的 FetchOptions 设置,即 RowSetSize=50。通过网络服务器获取的数据集中有 425,000 行。

FDTable 按预期执行。它只加载 50 个元组,而且几乎是立即完成的。当按 Ctrl+End 到达 DBGrid 显示的末尾时,它仅加载 100 个元组。滚动时它很少加载超过 100 个元组。对内存的影响可以忽略不计。但是滚动很慢。

FDQuery 加载 50 个元组,但需要大约 35 秒才能完成,并在此过程中消耗超过 0.5GB 的内存。如果您按 Ctrl+Home 移动到连接的 DBGrid 的末尾,它几乎会立即执行此操作,并在此过程中加载整个 table 并进一步消耗 700MB 内存。

我也尝试过 CachedUpdates。上面的结果关闭 CachedUpdates。打开时,对 FDQuery 的性能没有任何影响(仍然很差),但是对于 FDTable 它导致它在启动时加载整个 table,占用了半个时间分钟并消耗 1.2GB 内存。

看起来 fmOnDemand 模式实际上只适用于 FDTableCachedUpdates 关闭并且根本不适合 table 与 FDQuery 一起使用.

我使用 fmOnDemand 与 postgreSQL 和 MySQL 的测试结果基本相同。使用 FDTable fmOnDemand 只下载它需要的内容限制在 RowSetSizeRowSetSize 为 50 时,它最初会下载 50 个元组,无论你滚动到哪里,它都不会下载超过 111 个元组(尽管这无疑取决于连接的 DBGrid 的大小。如果你断开连接FDTable 它最初从数据源下载 50 个元组,如果您随后导航到基础 table 中的任何记录,它只下载一个元组并丢弃所有其他数据。

FDQuery in fmOnDemand 在打开时仅下载最初的 50 个元组,但如果您按 RecNo 导航,它会下载中间的每个元组。我更希望它会使用 LIMIT 和 OFFSET 命令来只获取被请求的记录。

要为 PostGre 重新创建测试,您需要以下 FireDAC 组件:

object FDConnectionPG: TFDConnection
Params.Strings = (      
  'Password='
  'Server='
  'Port='
  'DriverID=PG')
ResourceOptions.AssignedValues = [rvAutoReconnect]
ResourceOptions.AutoReconnect = True    
end

object FDQueryPG: TFDQuery
Connection = FDConnectionPG
FetchOptions.AssignedValues = [evMode, evRowsetSize]    
end

object FDTable1: TFDTable
CachedUpdates = True
Connection = FDConnectionPG
FetchOptions.AssignedValues = [evMode, evRowsetSize, evRecordCountMode]
FetchOptions.RecordCountMode = cmFetched
end

如果你想用 MYSQL 重新创建它,你基本上需要相同的 FireDAC 组件,但 FDConnection 需要设置如下:

object FDConnectionMySql: TFDConnection
Params.Strings = (
  'DriverID=MySQL'
  'ResultMode=Use')
ResourceOptions.AssignedValues = [rvAutoReconnect]
ResourceOptions.AutoReconnect = True   
end

您需要一个编辑框、两个按钮、一个复选框、一个计时器和一个标签以及以下代码:

procedure TfrmMain.Button1Click(Sender: TObject);
begin
if not FDQueryPG.IsEmpty then
  begin
    FDQueryPG.EmptyDataSet;
    FDQueryPG.ClearDetails;
    FDQueryPG.Close;
  end;

if not FDTable1.IsEmpty then
  begin
    FDTAble1.EmptyDataSet;
    FDTable1.ClearDetails;
    FDTable1.Close;
  end;


lFetched.Caption := 'Fetched 0';
lFetched.Update;

if cbTable.checked then
  begin
    FDTable1.TableName := '[TABLENAME]';
    FDTable1.Open();
    lFetched.Caption := 'Fetched '+ FDTable1.Table.Rows.Count.ToString;
  end
else
  begin
    FDQueryPG.SQL.Text := 'Select * from [TABLENAME]';        
    FDQueryPG.open;
    lFetched.Caption := 'Fetched '+ FDQueryPG.Table.Rows.Count.ToString;
  end;
timer1.Enabled:=true;    
end;

procedure TfrmMain.Button2Click(Sender: TObject);
begin
  if cbTable.Checked then
   FDTable1.RecNo := strToInt(Edit1.Text)
  else
   FDQueryPG.RecNo := strToInt(Edit1.Text);
end;

procedure TfrmMain.cbTableClick(Sender: TObject);
begin
  timer1.Enabled := False;
end;

procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
  if cbTable.checked then
    lFetched.Caption := 'Fetched '+ FDTable1.Table.Rows.Count.ToString
  else        
    lFetched.Caption:='Fetched '+FDQueryPG.Table.Rows.Count.ToString;
  lFetched.Update;
end;