FireDAC 数据库开发笔记 (第三章 使用内存数据组件 FDMemTable)
-
TFDMemTable
📌 简单的说TFDMemTable组件是把数据快储在内存中进行处理、
TFDMemTable组件中的数据基本上是和后端的数据源是 隔离 的
💡 FDMemTable组件一般是使用在下面的场景中
1. 把一些少量但经常会使用的数据放在TFDMemTable组件中,可提供最快速的数据处理速度,例如邮政编码查询,产品查询等。
2. 使用SOAP/REST取得的数据放在TFDMemTable组件中可提供最佳的速度。
3. 使用TFDQuery组件取得的数据再拷贝到TFDMemTable组件中进行处理。
4. 频繁异动的数据可暂时快储在TFDMemTable组件中,等待所有异动完成之后再一次更新回后端。
📌 TFDMemTable 组件也可以把它包含的内存数据以不同的格式储存到档案中,或是再从档案中再读取回来,TFDMemTable的SaveToFile和LoadFromFile方法
💡 TFDStanStorageJSONLink组件让这个App支持以以JSON的格式储存和读取数据
procedure TForml.FormCreate(Sender: TObject);
begin
if (not FindZipCodeFile) then
begin
CreateZipCodeTable;
FillZipCodeData;
SaveZipCodeFile;
end
else
begin
LoadZipCodeFile;
swLoadFromFile.IsChecked := True;
end;
end;
function TForml.GetZipCodeFile: String;
begin
Result := TPath.GetDocumentsPath + PathDelim + sZIPCODEFILE;
end;
procedure TForml.LoadZipCodeFile;
begin
fdmtZipCodes.LoadFromFile(GetZipCodeFile, sfJSON);
end;
procedure TForml.SaveZipCodeFile;
begin
fdmtZipCodes.SaveToFile(GetZipCodeFile, sfJSON);
end;
-
使用TFDMemTable处理SOAP/REST取得的数据
💡 网站或远程服务提供的这些数据源的数据转到TFDMemTable中之后就可以搭配数据感知组件或是LiveBidning技术来使用。
-
使用TFDMemTable处理数据
💡 因为TFDMemTable处理数据的速度很快
TFDMemTable共享TDataSet组件中的数据,程序员可以呼叫CloneCursor方法
FDMemTable 的 CloneCursor方法 :FDQuery 数据建立另外一个 DataView, FDQuery 的内容复制到 DMemTable 中
FDMemTable1.CloneCursor(fdqData, False, False)
CloneCursor方法接受3个参数
- 第1个参数是来源TDataSet组件
- 第2个参数AReset如果设定为True的话代表要设定TFDMemTable组件所有的特性值都将设定为内定值,由于通常我们是希望分享来源TDataSet组件的设定,因此一般情形下都是设定AReset为False
- 第3个参数AKeepSettings设定为True的话代表TFDMemTable的任何先前设定都不改变, 因此一般情形下也都是设定AKeepSettings为False.
💡 TFDMemTable的CopyDataSet方法可以从一个TDataSet中拷贝数据.
delphi 内存表 FDMemTable
📝 用好FDMemTable代替之前的ClientDataSet,以前ClientDataSet内存表转换太繁琐了步骤。
CopyDataSet
FDMemTable1.CopyDataSet(dm.ADOQueryPub, [TFDCopyDataSetOptions]) // FDCopyDataSetOptions -> coStructure, coRestart, coAppend);
多用FDMemTable,不再用ClientDataSetDataSetProvider1做转换了
FDMemTable2.Data := FDMemTable1.Data;
FDMemTable2.CopyDataSet(FDMemTable1, [TFDCopyDataSetOptions]); // FDCopyDataSetOptions coStructure, coRestart, coAppend
建立缓存表
FDMemTable1.Close();
FDMemTable1.FieldDefs.Clear();
FDMemTable1.FieldDefs.Add('ID', ftInteger, 0, True);
FDMemTable1.FieldDefs.Add('Name', ftString, 20, false);
FDMemTable1.CreateDataSet();
FDMemTable1.AppendRecord([101, 'aaa']);
FDMemTable1.AppendRecord([102, 'bbb']);
FDMemTable1.AppendRecord([103, 'ccc']);
数据集拷贝,复制数据集,合并数据集
FDMemTable1.Filter = 'id=102';
FDMemTable1.Filtered = true;
FDMemTable1只有1条记录
1)Data
FDMemTable2.Data = FDMemTable1.Data;
FDQuery.Open('select * from tt');
FDMemTable2.Data = FDQuery.Data;
FDMemTable2是全部记录,有3条记录。
FDMemTable1.Delete();后的记录不在Data里。
2)CopyDataSet
带结构拷贝
FDMemTable2.CopyDataSet(FDMemTable1, [TFDCopyDataSetOptions]) // TFDCopyDataSetOptions -> coStructure, coRestart , coAppend);
FDMemTable2.CommitUpdates();
缓存更新用到changeCount,所以copy完后加上CommitUpdates
不带结构,仅拷贝数据,字段个数可以不一致,字段数以目标数据集 FDMemTable2为标准。
FDMemTable2.CopyDataSet(FDMemTable1, [TFDCopyDataSetOptions]) // TFDCopyDataSetOptions -> coRestart , coAppend)
//第二个参数默认是 coRestart, coAppend,所以下面就更简单了。
FDMemTable2.CopyDataSet(FDMemTable1);
FDMemTable2只有1条记录
3) CopyRecordCopyField
copy the current record field values .Only One Record
FDMemTable2.CopyDataSet(FDMemTable1, TFDCopyDataSetOptions.coStructure);
FDMemTable2.Edit();
FDMemTable2.CopyRecord(FDMemTable1);
4) CloneCursor
All Record, ignore filter
CloneCursor,数据共享,一个修改,另一个也修改了,但是FDMemTable1.Close之后FDMemTable2还正常显示。
FDMemTable2.CloneCursor( FDMemTable1);
2016.3.4 test 不能排序,FDMemTable2.IndexFieldNames = 'ID' ; // 不起作用,所以尽量不建议用这个方式。
5) AtrachTable
All Record,ignore filter,TFDDatSTable
FDMemTable2.AttachTable(FDMemTable1.Table, NULL);
//or
//FDMemTable2.AttachTable(FDMemTable1.Table,FDMemTable1.View);
FDMemTable2.Open();
6) FilteredData
FDQuery1.Filter := 'upper(name) like ''D%''';
FDQuery1.Filtered := True;
// copy to FDMemTable1 all FDQuery1 visible (where name starts from D) records
FDMemTable1.Data := FDQuery1.FilteredData;
RecordCount是过滤后的记录数。过滤前有100行,过滤后有5行,那么RecordCount就是5
FDQuery1.Data是100行,FilteredData是5行的数据
7) XMLData
All Record ignore filter
Memo1.Text = FDMemTable1.XMLData;
FDMemTable2.XMLData = Memo1.Text;
FDMemTable1.ChangeCount,
通过Data赋值,默认全部记录都是修改过的,也就是ChangeCount=RecordCount,有100条记录,获取ChangeCount属性就是100.这样合适还是不合适呢?
FDMemTable1.Data = FDQuery.Data;
FDMemTable1.CancelUpdates(); 或者 FDMemTable1.CommitUpdates();
加上CancelUpdates这句话,ChangeCount就正常了!!反应真实的修改记录数。
Append的记录需要把属性CachedUpdates设为true,ChangeCount就正确了。
8) Delta
IFDDataSetReference类型
FDMemTable2.Delta= FDMemTable1.Delta;
ADMemTable1.FilterChanges := [rtModified, rtInserted, rtDeleted];
ADMemTable1.Data := ADQuery1.Delta;
9) 查看删除过的记录UpdateStatus
FDMemTable想要找到并显示删除的记录
FDMemTable1.Delete();
FDMemTable2.FilterChanges, Firedac.Comp.Dataset.rtDeleted;
FDMemTable1.Data = FDMemTable1.Data;
while (!FDMemTable1.eof)
{
if( table.UpdateStatus() == usDeleted)
...
}
默认是不显示删除过的记录的,FilterChanges不包括rtDeleted属性。
10) 分页及加载全部页
FetchOptions的Mode默认是fmOnDemand表示分页,每页50,改为fmAll表示全部记录。
分页 TFDFetchOptions.RowsetSize
FetchNext
FetchAll
FetchOptions.RecordCountMode property
FDQuery1.Open;
FDQuery1.FetchAll; //必须加这一句,否则数据集不全。
FDMemTable1.Data := FDQuery1.Data;
用了grideh,为何导致分页不灵了???全部记录出来了?field设置了ftsum导致,不设置每次就50行记录。
11)增强的Locate功能LocateEx、LookupEx函数
lxoCheckOnly If included, then LocateEx does not:
Change the current position. Fire BeforeScroll / AfterScroll events. Finish editing mode
不改变位置和编辑状态的搜索,强大!
if (ds1.LocateEx('DM', '001', TFDDataSetLocateOptions() -> lxoCheckOnly))
12)刷新数据集
query1.Refresh();
13)只读字段
select '' as temp, flag = 0, sql返回的虚拟字段,以前clientDataSet可以修改,FDMemTable里不能改了。
ClientDataSet1.FieldByName('flag').AsString = '1';
但是FDMemTable不能改了。怎么办?以前的这种虚拟字段的方式挺好用啊。
解决办法:设置属性TFDMemTable.UpdateOptions.CheckReadonly = true
14)主从表关系
第一步:
fdqueryDetail.MasterSource : = DataSource1;
第二步:
fdqueryDetail.MasterFields : = 'OrderNo'; { 多个字段时用分号隔开 }
或者
fdqueryDetail.sql.text = 'select * from OrderDetail where OrderMasterKey =: OrderMasterKey' ;
两个FDMemTable做主从怎么不起作用呢?
15)过滤数据FilterChanges
只显示修改后的数据
ClientDataSet1.FilterChanges = TFDUpdateRecordTypes() -> Firedac :: Comp :: Dataset :: rtModified;
最终解决了,原来是旧的ehlib控件卸载不干净,原来的工程里还有路径和ehlib.lib文件的链接,清除后排序全部OK!
16) 已有数据的FDMemTable添加列,动态添加列
原有数据集,现有数据集,现有字段,原有字段,添加新增选择列
FDMemTable2.FieldDefs : = FDMemTable1.FieldDefs;
FDMemTable2.FieldDefs.Add('Test', ftString, 20 { , False } ); // default parameter
FDMemTable2.FieldDefs.Find('Test').Index : = 0;
FDMemTable2.CreateDataSet; // or just Open that sets Active to true;
FDMemTable2.CopyDataSet(FDMemTable1);
17) 缓存更新
FDMemTable1.ChangeCount
执行了ApplyUpdates或CommitUpdates后ChangeCount变为0
FDMemTable1.ApplyUpdates(0);
升序
self.FDMemTable1.IndexFieldNames : = 'ID:A';
降序
self.FDMemTable1.IndexFieldNames : = 'ID:D';
FDMemTable1.IndexFieldNames = 'ID:D';
FDMemTable1.CloneCursor(m.dsModule, true, true);
CloneCurso的数据集均无法用IndexFieldNames排序。用CreateDataSet创建的数据集排序正常!
Data赋值、CopyDataSet后的数据集均排序可以正常。建议不用CloneCursor。
18. 修改只读字段
query1.UpdateOptions.AssignedValues = [uvCheckReadOnly]
FDStoredProc1.UpdateOptions.CheckReadOnly = false;
19. 建立索引
FDQueryRelation.AddIndex('index1','ITEMTYPE;ITEMNOHIS','', []);
FDQueryRelation.IndexName : = 'index1';
经过试验证明,filter没有使用索引,支持like表达式
locate和findkey使用了索引,精确定位,要求检索速度快,检验使用
Caching_Updates
FDQuery1.CachedUpdates : = True;
FDQuery1.Append;
...
FDQuery1.Post;
FDQuery1.Append;
...
FDQuery1.Post;
FDQuery1.Append;
...
FDQuery1.Post;
FDConnection1.StartTransaction;
iErrors : = FDQuery1.ApplyUpdates;
if iErrors = 0 then begin
FDQuery1.CommitUpdates;
FDConnection1.Commit;
end
else
FDConnection1.Rollback;
var
oErr: EFDException;
...
if FDQuery1.ApplyUpdates > 0 then begin
FDQuery1.FilterChanges : = [rtModified, rtInserted, rtDeleted, rtHasErrors];
try
FDQuery1.First;
while not FDQuery1.Eof do begin
oErr : = FDQuery1.RowError;
if oErr <> nil then begin
// process exception object
...
end;
FDQuery1.Next;
end;
finally
FDQuery1.FilterChanges : = [rtUnmodified, rtModified, rtInserted];
end;
end;
AppendData
2个完全一样的数据集合并 AppendData
ADOQuery1.AppendData(ADOQuery2.Data );
不用循环遍历了,一行命令搞定
20. fdmemtable批量编辑性能优化
FDMemTable1.LogChanges : = False;
FDMemTable1.FetchOptions.RecsMax : = 300000; //Sample value
FDMemTable1.ResourceOptions.SilentMode : = True;
FDMemTable1.UpdateOptions.LockMode : = lmNone;
FDMemTable1.UpdateOptions.LockPoint : = lpDeferred;
FDMemTable1.UpdateOptions.FetchGeneratorsPoint : = gpImmediate;
FDMemTable1.BeginBatch;
try
for i : = 1 to 1000 do begin
FDMemTable1.Append;
// ...
FDMemTable1.Post;
end;
finally
FDMemTable1.EndBatch;
end;
复制表有两种方法
DataTable.Clone() 只克隆表结构
DataTable.Copy() 克隆表结构及数据
// 9行7列的值。
FDMemTable1.Data.DataView.Rows.ItemsI[9].ValueI[7];
FDMemTable1.Table.Rows[i].ValueI[oCol.Index]
Caption = FDMemTable1 -> SourceView -> Rows -> Count; //过滤后1条
Caption = FDMemTable1 -> Table-> Rows -> Count; //过滤无效,全部记录3条