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. 第1个参数是来源TDataSet组件
  2. 第2个参数AReset如果设定为True的话代表要设定TFDMemTable组件所有的特性值都将设定为内定值,由于通常我们是希望分享来源TDataSet组件的设定,因此一般情形下都是设定AReset为False
  3. 第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条
posted @ 2022-05-25 19:03  麦麦提敏  阅读(2510)  评论(0编辑  收藏  举报