4.3.4 有效地查寻数据
在本小节中将使用一个范例来观察并且改善查寻数据的速度,并且也会观察这些不同的技术对于结果数据集中数据数量的影响。在下面的内容中你将会发现有一些技术可以大幅地改善执行效率,不过天下没有白吃的午餐,虽然这些技术可以加快执行效率,但是也会产生其他的副作用,开发人员必须根据本身的需求并结合不同的技术来达到最佳的效果。
首先让我们从最基础的地方开始,图4-23是本小节使用的范例应用程序主窗体。在这个主窗体中有Locate、Lookup、Filter等的按钮,它们分别使用TClientDataSet的Locate、Lookup、Filter方法查寻特定的数据,而Smart Locate、Smart Locate II则是稍后讨论的增加查寻效率的按钮。加下角的TEdit会显示使用各种不同的查寻方法时在结果数据集中产生的记录数量。本范例查寻的对象是一个包含一万笔数据的数据表。

图4-23 查寻数据范例的主窗体
首先使用Locate来查寻数据,不过先要查寻数据是属于大量数据之中前端的数据。在本范例中TClientDataSet的PacketRecords特性值是设定为10的。
使用TClientDataSet的Locate,Lookup和Filter方法查寻数据
范例应用程序主窗体中Locate按钮使用了下列的程序代码查寻范例数据表中键值字段ID,现在你应该很熟悉Locate的语法,因此就不再多说了。
procedure TfrmFindData.btnLocateClick(Sender: TObject);
begin
GetStartTime;
dmFindData.sqlcdsTest.Locate('ID', edtID.Text, [loCaseInsensitive,
loPartialKey]);
GetEndTime;
AppMsg('Locate', GetRunTime);
edtTime1.Text := FloatToStr((GetRunTime) / 1000.0);
edtRecordCount.Text := IntToStr(dmFindData.sqlcdsTest.RecordCount);
end;
现在执行应用程序,在主窗体中的Locate按钮旁的TEdit控件中输入已经存在TClientDataSet前10笔的A025800680这笔数据。由于这笔数据已经存在于结果数据集中(请参考前图,它位于TClientDataSet一开始的第10笔数据),因此我们会认为TClientDataSet应该可以瞬间找到这笔数据,并且不需要再从后端数据来源中取得额外的数据。
图4-24是点击Locate按钮执行查寻数据的结果,从图中我们发现TClientDataSet并不是这样查寻数据,而是先从后端数据来源中取得所有的数据,再进行查寻的工作,因此居然需要花上2.803秒的时间查寻已经在我们眼帘范围之内的数据。说实话这种执行行为实在非常地愚蠢,不过这是Locate、Lookup、Filter等方法内定的行为。尽管如此,聪明的开发人员却可以改善。
在我们继续讨论之前,先看看Locate、Lookup和Filter等方法在执行效率上有没有什么差别。表4-8是使用不同的方法查寻相同的数据结果,从表中我们可以知道它们的查寻效率几乎没有什么明显的差别。

图4-24 使用Locate方法查寻数据的结果
表4-8
|
|
Locate |
Lookup |
Filter |
|
查寻时间 |
2.803 |
2.856 |
2.901 |
不过当所有的数据下载到TClientDataSet的结果数据集之中后,查寻数据的速度便明显地加快了许多。例如如果我们再次查寻A025800680这笔数据,那么 Locate便只需要花0.01秒的时间,相当快速。接下来的目标便是改善Locate初次查寻数据的效率。
要改善Locate等查寻数据的效率的第一种方法是想办法切断TClientDataSet把后端数据来源中所有数据一次下载到客户端的执行行为。让TClientDataSet在已经存在于结果数据集之中的数据先进行查寻,例如A025800680这笔数据已经存在于结果数据集中,因此如果可以让TClientDataSet先直接在结果数据集中查寻,那么就可以大幅增加效率。
但是要如何切断TClientDataSet和后端数据来源的连接呢?TClientDataSet已经提供了一种方法,那就是CloneCursor。
使用CloneCursor查寻数据
TClientDataSet/TSimpleDataSet的CloneCursor方法可以让另外一个TClientDataSet/TSimpleDataSet分享相同的数据。但是CloneCursor另外一个没有说明的功能则可以避免TClientDataSet/TSimpleDataSet从后端数据来源下载所有的数据,借助这个隐藏的功能,我们可以先使用CloneCursor方法在结果数据集中查寻数据,如果没有发现需要的数据,那么再让原先的TClientDataSet/TSimpleDataSet使用Locate、Lookup方法继续查寻数据。这样对于查寻已经存在于结果数据集中的数据而言是非常有效率的。
因此范例应用程序的Smart Locate按钮便使用了下面的程序代码来查寻数据:
procedure TfrmFindData.btnSmartLocateClick(Sender: TObject);
var
aCDS : TClientDataSet;
begin
aCDS := TClientDataSet.Create(Self);
try
GetStartTime;
aCDS.CloneCursor(dmFindData.sqlcdsTest, True);
if aCDS.Locate('ID', edtID.Text, [loCaseInsensitive, loPartialKey]) then
dmFindData.sqlcdsTest.MoveBy(aCDS.RecNo - dmFindData.sqlcdsTest.RecNo)
else
dmFindData.sqlcdsTest.Locate('ID', edtID.Text, [loCaseInsensitive,
loPartialKey]);
GetEndTime;
AppMsg('Locate', GetRunTime);
edtTime2.Text := FloatToStr((GetRunTime) / 1000.0);
edtRecordCount.Text := IntToStr(dmFindData.sqlcdsTest.RecordCount);
finally
aCDS.Free;
end;
end;
上面的程序代码首先建立一个TClientDataSet控件aCDS,再调用CloneCursor方法从原先的sqlcdsTest这个TSimpleDataSet控件取得另外一个cursor,然后调用aCDS的Locate在结果数据集中查寻数据。由于aCDS会避免sqlcdsTest从后端数据来源下载所有的数据,因此它的查寻速度非常良好。图4-25便是使用CloneCursor查寻A025800680这笔数据的结果,我们可以看到它只需要0.004秒,比起直接使用sqlcdsTest的Locate快了数百倍。






