3.1.4 排序时应用的考虑
答案是“是的,程序员可以选择适合的技巧来排序数据,但是在排序数据时,程序员必须注意排序数据实际的执行特性”。这是什么意思呢?简单地说,TSimpleDataSet是把dbExpress目前存取到的数据储存在TSimpleDataSet管理的缓冲存储器中。由于TSimpleDataSet可以采用分段的方式存取数据,因此,可能后端数据库中拥有大量的数据,而TSimpleDataSet目前只有少数的数据。但是程序员一旦使用前面介绍的方法排列数据时,TSimpleDataSet会先把后端所有的数据存取到客户端,再进行排序数据的工作。这会造成数项不利的后果:
n 把大量数据存取到前端会造成网络瞬间的大量负荷,使所有客户端应用程序的执行缓慢,降低网络的使用率。
n 客户端应用程序可能因为内存不足而造成应用程序错误。
n 需要花费大量的时间,使应用程序反应迟钝。
n 可能不是用户想要的结果。
用户可能只想要排序目前在TSimpleDataSet中的数据,而不是所有后端的数据,因此,这样做不但没有效率,而且用户也不会接受。下面就让我们以一个实际的范例来说明。图3-10显示的就是我们叙述的情形,在后端数据表中拥有20 000条数据,假设现在用户只想为目前在TSimpleDataSet中的数据进行排序的工作,因此,使用了下面的程序代码来排序数据。当然这些程序代码是使用前面介绍的排序技巧。
procedure TfrmPerfMain.btnAddIndexClick(Sender: TObject);
var
sIndexName : String;
begin
LogStartTime;
// dmDBExpress.sqlmTest.Active := True;
sIndexName := AddIndexForSCDS(edtSortFields.Text, rbAscending.Checked);
dmDBExpress.cdsTest.IndexName := sIndexName;
ledtRecordCount.Text := IntToStr(dmDBExpress.cdsTest.RecordCount);
LogEndTime;
LogRunTime(mmSorts, 'AddIndex Sort' + IntToStr(ILOOPS) + '笔数据时间 : ');
end;
但是在上面的程序代码执行之后,从图3-10下方TClientDataSet的数据笔数中可以看到,这个范例程序已经把所有20 000笔数据存取到客户端,而且花费了2.374秒,更麻烦的是用户如何在20 000笔排序的数据中找到他原先想要看到的数据?

图3-10 TSimpleDataSet/TClientDataSet在排序数据时会把所有后端的数据存取到前端,再进行排序的工作,这花费大量的时间
因此,程序员应该尽量避免直接对TSimpleDataSet/TClientDataSet进行数据排序的工作,以避免拖垮dbExpress客户端应用程序的执行效率。但是如果程序员只需要对目前存在于TSimpleDataSet/TClientDataSet中的数据进行排序,那么程序员可以使用一点小技巧来达成目的,又不会从后端数据库中存取所有的数据到客户端。
这个技巧就是使用TSimpleDataSet/TClientDataSet的CloneCursor方法为TSimpleDataSet/TClientDataSet建立一个复制的Cursor管理机制,以切断TSimpleDataSet/TClientDataSet和后端数据来源的连接,再进行排序。这样一来,当复制的Cursor排序数据时,就不会从后端数据来源中取得所有的数据。CloneCursor方法有如下的原型:
procedure CloneCursor(Source :TCustomClientDataSet; Reset: Boolean; KeepSettings: Boolean = False); virtual;
它的第一个参数Source代表要复制的Cursor的来源TSimpleDataSet/TClientDataSet,第二和第三个参数则代表复制的特性。Reset和KeepSettings参数可以控制下面的来源TSimpleDataSet/TClientDataSet控件以及目的TSimpleDataSet/TClientDataSet控件的特性值和事件处理函数:
n Filter, Filtered, FilterOptions和OnFilterRecord。
n IndexName。
n MasterSource和MasterFields。
n ReadOnly。
n RemoteServer和ProviderName。
表3-2叙述了Reset和KeepSettings这两个参数设定值对于上面列出的特性值和事件处理函数的影响:
表3-2
|
Reset值 |
KeepSettings值 |
意义 |
|
False |
False |
目的TDataSet在前面列出的特性值都设定成和来源TDataSet一样的数值 |
|
True |
N/A |
目的TDataSet在前面列出的特性值都设定会被清除 |
|
False |
True |
目的TDataSet在前面列出的特性值都不会被改变 |
程序员可以通过设定Reset和KeepSettings这两个参数值来控制目的TSimpleDataSet/TClientDataSet的执行特性。
例如,下面的程序代码就是一个很好的范例。在下面的程序代码中,我们首先使用一个临时的TSimpleDataSet/TClientDataSet,并且调用它的CloneCursor方法以复制原本拥有数据的cdsTest这个TSimpleDataSet/TClientDataSet控件。由于cdsTest只是复制cdsTest的Cursor,因此,便和cdsTest连接的后端数据来源没有连接关系。在这之后,程序代码就只针对cdsTemp管理的数据进行新增索引的工作进行数据排序,这样一来,就可以避免从后端数据来源存取大量的数据到客户端的成本。
procedure TfrmPerfMain.Button1Click(Sender: TObject);
var
sIndexName : String;
begin
LogStartTime;
dmDBExpress.cdsTemp.CloneCursor(dmDBExpress.cdsTest, True, False);
sIndexName := INDEXID + edtSortFields.Text;
if (rbAscending.Checked) then
dmDBExpress.cdsTemp.AddIndex(sIndexName, edtSortFields.Text, [])
else
dmDBExpress.cdsTemp.AddIndex(sIndexName, edtSortFields.Text, [ixDescending]);
dmDBExpress.cdsTemp.IndexName := sIndexName;
ledtRecordCount.Text := IntToStr(dmDBExpress.cdsTemp.RecordCount);
LogEndTime;
LogRunTime(mmSorts, 'AddIndex Sort 1' + IntToStr(ILOOPS) + '笔数据时间 : ');
end;
使用上面的技巧之后,图3-11显示的是和排序图3-10中相同的数据。从图3-11中可以看到使用CloneCursor技巧之后,客户端的数据只有100笔,而且整个排序数据的速度提升为0.01秒。当然,这是因为现在我们只对目前存在于TSimpleDataSet/ TClientDataSet中的数据进行排序。

图3-11 通过小程序技巧可以避免TSimpleDataSet/TClientDataSet把所有的数据存取到前端之后才排序数据
除了使用CloneCursor之外,如果程序员希望排序的数据能够和原始的数据之间有着更清楚的分隔,那么程序员可以把拥有原始数据的TSimpleDataSet/TClientDataSet控件的Data特性值指定给另外一个独立的TSimpleDataSet/TClientDataSet控件,再针对另外独立的TSimpleDataSet/TClientDataSet控件进行数据排序。在本书前面已经说明过TSimpleDataSet/TClientDataSet控件的Data特性值包含的就是目前存在于TSimpleDataSet/TClientDataSet中的数据。
下面几点总结了程序员在使用TSimpleDataSet/TClientDataSet进行数据排序时应该牢记在心的事情:
n 默认情况下,当程序员针对TSimpleDataSet/TClientDataSet中的数据进行处理时,TSimpleDataSet/TClientDataSet都会尝试把所有后端的数据存取到客户端再进行处理,因此,程序员必须小心这个行为带来的后果。
n 如果程序员只想排序目前在TSimpleDataSet/TClientDataSet中的数据,那么就使用CloneCursor或是额外的TSimpleDataSet/TClientDataSet控件进行排序。
n 如果程序员想排序所有的数据,那么请直接改变TSimpleDataSet/ TClientDataSet的CommandText特性值,使用SQL语句的Order By来取得排序的数据,让后端的数据来源来帮助排序数据。






