ClientDataSet 探讨
1.
??? 與TTable、TQuery一樣,TClientDataSet也是從TDataSet繼承下來的,它通常用于多層體系結構的客戶端。很多數據庫應用程序都用了BDE,BDE往往給發布帶來很大的不便,因而TClientDataSet最大的特點是它不依賴于BDE(Borland Database Engine),但它需要一個動態鏈接庫的支持,這個動態鏈接庫叫DBCLIENT.DLL。在客戶端,也不需要用TDatabase構件,因為客戶端并不直接連接數據庫。由于TClientDataSet是從TDataSet繼承下來的,所以,它支持諸如編輯、搜索、瀏覽、糾錯、過濾等功能。由于 TClientDataSet在內存中建立了數據的本地副本,上述操作的執行速度很快。也正是由于TClientDataSet并不直接連接數據庫,因此,客戶程序必須提供獲取數據的機制。
在Delphi 4中,TClientDataSet有三種途徑獲取數據:
1、從文件中存取數據。
2、從本地的另一個數據集中獲取數據。
3、通過IProvider接口從遠程數據庫服務器獲取數據。
在一個客戶程序中,可以同時運用上述三種機制獲取數據。
和其他數據集構件一樣,可以用標準的數據控件顯示由TClientDataSet引入的數據集,當然,這需要借助于TDataSource構件。由于 TClientDataSet是從TDataSet繼承下來的,所以,凡是其他數據集構件支持的功能,TClientDataSet構件也大致具備。不同的是,TClientDataSet能夠在內存中建立數據的副本,因此,TClientDataSet比其他數據集構件增加了一些特殊的功能。
在運行期,可以調用諸如First、GotoKey、Last、Next和Prior等函數來瀏覽數據。TClientDataSet也支持書簽 (BookMark)功能,可以用書簽來標記某條記錄,以后就可以方便地找到這條記錄。對于TTable、TQuery等數據集構件來說,只能讀 RecNo屬性來判斷當前記錄的序號。對于TClientDataSet構件來說,還可以寫RecNo屬性,使某一序號的記錄成為當前記錄。
1、從文件中存取數據要從文件中讀取數據,可以調用LoadFromFile函數。LoadFromFile函數需要傳遞一個參數,用于指定文件名。文件名應包含完整的路徑。如果客戶程序總是從一個固定的文件中讀取數據,可以設置FileName屬性指定一個文件名,以后,當TClientDataSet 引入的數據集打開時,就自動從這個文件中讀取數據,不需要調用LoadFromFile。要從流中讀取數據,可以調用LoadFromStream。 LoadFromStream需要傳遞一個參數,用于指定一個流對象。注意:LoadFromFile(LoadFromStream)只能從先前用 SaveToFile(SaveToStream)保存的文件中讀取數據。要把數據保存到文件中,可以調用SaveToFile函數。 SaveToFile需要傳遞一個參數,用于指定文件名。如果指定的文件已存在,文件中的數據將被覆蓋。如果客戶程序總是把數據保存到一個固定的文件中,可以設置FileName屬性指定一個文件名,當TClientDataSet引入的數據集關閉時,就自動把數據保存到這個文件中,不需要調用 SaveToFile。要把數據保存到流中,可以調用SaveToStream。SaveToStream需要傳遞一個參數,指定一個流對象。注意:當把數據保存到文件或流中時,日志中記載的修改仍然保留。這樣,當下次調用LoadFromFile或LoadFromStream讀取數據時,仍然可以恢復原來的數據。
ClientDataSet強大的數據復制技術:
?? 通過ClientDataSet.Data屬性可以訪問客戶程序從應用服務器檢索到的數據。程序示例如下:
?? Procedure TForm1.Button1Click(Sender: TObject);
?? Begin
???? ClientDataSet1.Data :=
???? ClientDataSet1.Provider.DataRequest(FilterEdit.Text);//在Delphi4版本之上有所改變
?? End;
?? 也可以直接賦值:
?? ClientDataSet1.Data:=ClientDataSet2.Data;//(相當于把ClientDataSet2的數據拷貝給ClientDataSet1,是不是很方便)
?? 從其他數據集獲取數據(除ClientDataSet):
?? DataSetProvider1.DataSet:=DataSet;//DataSet代表一個數據集
?? ClientDataSet1.Data := DataSetProvider1.Data;
3. 排序
ClientDataSet排序
1、簡單排序
ClientDataSet1.IndexFieldNames:='排序字段'
2、復雜排序(建立索引)
下面這個過程僅供參考(因為用到三方控件DBGridEh):
procedure TDM1.DsSort(SortColumn: TColumnEh);
var
OldIndex:string;
begin
if (SortColumn.Grid.DataSource=nil) or (SortColumn.Grid.DataSource.DataSet=nil) or (not SortColumn.Grid.DataSource.DataSet.Active) then Exit;
OldIndex:=TClientDataSet(SortColumn.Field.DataSet).IndexName;
if OldIndex<>'' then
begin
?? TClientDataSet(SortColumn.Field.DataSet).IndexName:='';
?? TClientDataSet(SortColumn.Field.DataSet).DeleteIndex(OldIndex);
end;
case SortColumn.Title.SortMarker of
?? smNoneEh,
?? smUpEh?? :TClientDataSet(SortColumn.Field.DataSet).AddIndex('px',SortColumn.Field.FieldName,[ixDescending]);
?? smDownEh:TClientDataSet(SortColumn.Field.DataSet).AddIndex('px',SortColumn.Field.FieldName,[ixPrimary]);
end;
TClientDataSet(SortColumn.Field.DataSet).IndexName:='px';
end;
把上面的過程稍做修改,可用于標準DBGridvar
ASC:Boolean=True;//是否升序排列
procedure TDM1.DsSort(SortColumn: TColumn);
var
OldIndex:string;
begin
if (SortColumn.Grid.DataSource=nil) or (SortColumn.Grid.DataSource.DataSet=nil) or (not SortColumn.Grid.DataSource.DataSet.Active) then Exit;
OldIndex:=TClientDataSet(SortColumn.Field.DataSet).IndexName;
if OldIndex<>'' then
begin
TClientDataSet(SortColumn.Field.DataSet).IndexName:='';
TClientDataSet(SortColumn.Field.DataSet).DeleteIndex(OldIndex);
end;
case ASC of
Ture :TClientDataSet(SortColumn.Field.DataSet).AddIndex('px',SortColumn.Field.FieldName,[ixDescending]);//已經是升序就按降序排列
else//否則按升序排列
TClientDataSet(SortColumn.Field.DataSet).AddIndex('px',SortColumn.Field.FieldName,[ixPrimary]);
end;{end case}
TClientDataSet(SortColumn.Field.DataSet).IndexName:='px';
ASC:=not ASC;
end;
4. 提交更新過程:
首先,客戶程序要調用ApplyUpdates函數向應用服務器提出申請,ApplyUpdates函數將通過IProvider接口把Delta(數據變動情況)屬性傳遞給應用服務器。應用服務器收到客戶程序的申請后,再向遠程數據庫服務器提出申請,并且把被遠程數據庫服務器認為出錯的記錄暫時緩存起來。應用服務器上的TDataSetProvider或TProvider構件把出錯的記錄返回給客戶程序,其中包括錯誤信息和錯誤代碼。客戶程序收到這些出錯的記錄后,可以進行核對和修改,然后繼續更新。注意:如果應用服務器端使用MTS類型的遠程數據模塊,就無法提供IProvider接口,這種情況下,必須通過遠程數據模塊的接口直接申請更新數據。
if ClientDataSet1.ChangeCount>0 then//有未決的修改
?? ClientDataSet1.ApplyUpdates(MaxErrors);//將修改提交到服務器
參數MaxErrors用于指定一個最大錯誤數,如果出錯的記錄數超過了這個參數的值,此次更新就停止。如果MaxErrors參數設為0,只要應用服務器發現有一個錯誤的記錄,更新操作就停止。如果MaxErrors參數設為-1,當應用服務器發現有錯誤的記錄,就嘗試更新下一個記錄,等所有的記錄都嘗試過以后才返回。ApplyUpdates函數將返回實際遇到的錯誤數,同時,應用服務器將返回那些有錯誤的記錄。
當應用服務器收到客戶的提交請求后,觸發OnUpdateData,這時就可以對客戶提交的數據進行檢查和編輯:
如
Procedure TDataModule1.Provider1UpdateData(Sender:TObject;DataSet: TClientDataSet);
Begin
With DataSet Do
Begin
??? First;
??? While not Eof Do
??? Begin
????? If UpdateStatus = usInserted then
????? Begin
??????? Edit;
??????? FieldByName('DateCreated').AsDateTime := Date;
??????? Post;
????? End;
????? Next;
??? End;
End;
End;
然后將編輯后的數據提交到數據庫服務器。
ClientDataSet1.CancelUpdates;//恢復所有修改過但未提交(包括提交未成功的)的記錄
ClientDataSet1.UndoLastChange;//恢復前一次的修改,相當于Undo功能
注意使用這種提交方式(ApplyUpdates)在查詢時盡可能避免使用數據處理(關聯、分組、求和等等等),否則不能提交(除非自己寫一些特殊處理程序)
在三層結構中,TClientDataSet的地位是不可估量的,她的使用正確與否,是十分關鍵的,本文從以下幾個方面闡述她的使用,希望對你有所幫助.
1.動態索引
procedure TForm1.DBGrid1TitleClick(Column: TColumn);
begin
if (not column.Field is Tblobfield) then//Tblobfield不能索引,二進制
ClientDataSet1.IndexFieldNames:=column.Field.FieldName;
end;
2.多層結構中主從表的實現
設主表ClientDataSet1.packetrecord為-1,所有記錄
設從表ClientDataSet1.packetrecord為0,當前記錄
3.Taggregates使用
(1)在字段編輯中add new field類型為aggregates
后設置expression(表達試)
設置active:=true即可
使用dbedit的field為前者即可
(2)使用Aggergates屬性add設計表達試
調用
showmessage(floattostr(ClientDataSet1.Aggregates.Count));
showmessage(ClientDataSet1.Aggregates.Items[0].Value);
4.在單層數據庫中不要BDE
使用ClientDataSet代替table,使用ClientDataSet的loadfilename裝入cds
代替table的tablename的db或者dbf
原來的程序改造方法:
加一個ClientDataSet,使用右鍵assign locate data
后savetofile,再loadfromfile,后刪除table
將原連table的datasource設為ClientDataSet
唯一注意的是:要將midas.dll拷到system或者當前目錄
5.三層結構的公文包的實現方法
同時設定1:filename(*.cds)2.remote server
6.可以對data賦值(從另一個數據集取值)
ClientDataSet2.Data:=ClientDataSet1.Data;
ClientDataSet2.Open;
或者
ClientDataSet2.CloneCursor(ClientDataSet1,true);
ClientDataSet2.Open;
7.附加數據取得
客戶程序向應用服務器請求數據。如果TClientDataSet 的
FetchOnDemand 屬性設為True,
客戶程序會根據需要自動檢索附加的數據包如BLOB字段的值或嵌套表的內容。
否則,
客戶程序需要顯式地調用GetNextPacket 才能獲得這些附加的數據包。
ClientDataSet的packetrecords設置一次取得的記錄個數
8.ClientDataSet與服務器端query連接方法
(1)sql內容為空
ClientDataSet1.Close;
ClientDataSet1.CommandText:=edit1.Text;//即sql內容
ClientDataSet1.Open;
對于沒有應用服務器設置filter 如:country like 'A%'
filtered=true可實現sql功能
(2)有參數
如服務端query的sql為
select * from animals
where name like :dd
則:客戶端ClientDataSet
var
pm:Tparam;
begin
ClientDataSet1.Close;
ClientDataSet1.ProviderName:='DataSetProvider1';
pm:=Tparam.Create(nil);
pm.Name:='dd';
pm.DataType:=ftString;
ClientDataSet1.Params.Clear;
ClientDataSet1.Params.AddParam(pm);
ClientDataSet1.Params.ParamByName('dd').AsString:=edit1.Text ;
ClientDataSet1.Open;
pm.Free;
end;
9.數據的更新管理
(1)savepoint 保存目前為止數據狀態,可以恢復到這個狀態
var
pp:integer;
begin
pp:=ClientDataSet1.SavePoint;
ClientDataSet1.Edit;
ClientDataSet1.FieldByName('姓名').asstring:='古話';
ClientDataSet1.Post;
table1.Refresh;
end;
恢復點
ClientDataSet1.SavePoint:=pp;
(2)cancel,RevertRecord
取消對當前記錄的修改,只適合沒有post的,如果post,調用
RevertRecord
(3)cancelupdate
取消對數據庫所有的修改
(4)UndoLastChange(boolean),changecount
取消上一次的修改,可以實現連續撤消
參數為true:光標到恢復處
false:光標在當前位置不動
changecount返回修改記錄的次數,一個記錄修改多次,返回只一次
但UndoLastChange只撤消一次
10.可寫的recno
對于Ttable和Tquery的recno是只讀的,而TClientDataSet的recno可讀可寫
ClientDataSet1.recno:=5;是設第五個記錄為當前記錄
11.數據保存
對于table使用post可更新數據
而ClientDataSet1的post只更新內存數據,要更新服務器數據要使用
ApplyUpdates(MaxErrors: Integer),他有一個參數,是允許發出錯誤的
次數,-1表示無數次,使用simpleobjectbroker時常設為0,實現自動容錯和負載平衡
?
ClientDataSet使用心得和技巧
影響ClientDataSet處理速度的一個因素
TClientDataSet是Delphi開發數據庫時一個非常好的控件。有很強大的功能。
我常常用ClientDataSet做MemoryDataSet來使用。還可以將ClientDataSet的數據保存為XML,這樣就可以做簡單的本地數據庫使用。還有很多功能就不多說了。在使用ClientDataSet的過程中關于怎樣提高處理速度這個問題,我就我個人的一點點體會和大家分享一下。
通常情況下我們一般都是用
...ClientDataSet-->DataSource-->DBComponent
這樣的結構,處理數據的時候就直接操作ClientDataSet。但是大多DBComponet都會立即響應ClientDataSet的變化。如果你是向ClientDataSet中插入很多數據時候,DBComponent就要響應幾次,而且響應過程根據不同的控件,速度,過程數量都不一樣。這樣就影響了程序的執行效率 。所以在對ClientDataSet處理中,我是用ClientDataSet.DisableControls和ClientDataSet.EnableControls 方法:打開和關閉DBComponent與ClientDataSet的數據顯示關系。
例如:
ClientDataSet..DisableControls;
...
for I := 0 to 10000 do
begin
ClientDataSet.Append;
...
ClientDataSet.Post;
end;
...
ClientDataSet.EnableControls
...
這樣做以后你會發現處理速度比以前沒有使用方法的時候有成倍的提高。
ClientDataSet的數據查找。
我所介紹的心得和技巧都是用ClientDataSet來做范例,也可以應用于其他的一些DataSet。廢話就不多說了。我們還是先看代碼,讓后再總結。
1.Scanning 掃描數據查找
這是最簡單最直接也是最慢的一種方法,遍歷所有數據:
procedure TForm1.ScanBtnClick(Sender: TObject);
var
Found: Boolean;
begin
Found := False;
ClientDataSet1.DisableControls;
Start;
try
ClientDataSet1.First;
while not ClientDataSet1.Eof do
begin
if ClientDataSet1.Fields[FieldListComboBox.ItemIndex].value =
SearchText then
begin
Found := True;
Break;
end;
ClientDataSet1.Next;
end;
Done;
finally
ClientDataSet1.EnableControls;
end;
if Found then ShowMessage(SearchText +
' found at record ' + IntToStr(ClientDataSet1.RecNo))
else
ShowMessage(ScanForEdit.Text + ' not found');
end;
2.Finding 尋找數據
最老,但是最快 的查找方式。
使用FindKey/FindNearest來查找一條或多條符合條件的數據,當然待查找的Field必須是一個IndexField 。可以看出,這種基于Index的查找速度是非常快的。
procedure TForm1.FindKeyBtnClick(Sender:
TObject);
begin
Start;
if ClientDataSet1.FindKey([SearchText]) then
begin
Done;
StatusBar1.Panels[3].Text := SearchText +
' found at record ' +
IntToStr(ClientDataSet1.RecNo);
end
else
begin
Done;
StatusBar1.Panels[3].Text :=
SearchText + ' not found';
end;
end;
procedure TForm1.FindNearestBtnClick(Sender: TObject);
begin
Start;
ClientDataSet1.FindNearest([SearchText ]);
Done;
StatusBar1.Panels[3].Text := 'The nearest match to ' +
SearchText + ' found at record ' +
IntToStr(ClientDataSet1.RecNo);
end
3.Going 定位
GotoKey/GotoNearest 與FindKey/FindNearest基本上沒有什么區別。它也是基于Index的查找。唯一的區別就是在于你是怎么定義你的查找了。代碼上也有區別:
ClientDataSet1.SetKey;
ClientDataSet1.FieldByName(IndexFieldName).value := SearchText;
ClientDataSet1.GotoKey;
就相當于
ClientDataSet1.FindKey([SearchText]);
要用好這兩種基于Index的查找,還需要了解ClientDataSet和Index機制。這里就不詳細說明Index機制。一個基本的原則,要有Index,才能查找。
4.Locating 查找數據
2,3兩種查找方式都是基于Index的,但是在實際應用中,可能會查找IndexField以外的Field。那我們就可以使用Locate。但是查找速度是沒有2,3兩種快的。比如:如果你查找一條紀錄9000/10000,Locate需要500ms,Scanning需要>2s,FindKey只要10ms(但是當你打開ClientData的時候,建立Index需要1s)。
procedure TForm1.LocateBtnClick(Sender:
TObject);
begin
Start;
if ClientDataSet1. Locate('Field1,Field2..',VarArrayOf['value1,value2..'], []) then
begin
Done;
StatusBar1.Panels[3].Text :=
'Match located at record ' +
IntToStr(ClientDataSet1.RecNo);
end
else
begin
Done;
StatusBar1.Panels[3].Text := 'No match located';
end;
end;
小結:
ClientDataSet提供了好多種查找數據的方法。但是各自有其優缺點。
上面的例子中有Start;和Done,如果你有興趣,可以加入計時點進行速度測試。
Scanning最簡單,但是最慢,因為比較慢,還得使用ClientDataSet.DisableControls和ClientDataSet.EnableControls方法(我在前面一片文章講過)。
Findkey/FindNearest(GotoKey/GotoNearest)代碼多,但是非常快。必須使用Index,不同的是Find需要的Index是必須建立好的,而Goto可以在第一次使用時建立Index。
Locate使用最方便,不需要Index,但是速度沒有Find快。
轉載于:https://www.cnblogs.com/lzj1981/archive/2013/05/01/3053261.html
總結
以上是生活随笔為你收集整理的ClientDataSet 探讨的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 删除系统Win7系统盘越来越小,系统盘清
- 下一篇: latex测试