有关李维著作中的Q(100分)

  • 主题发起人 主题发起人 sanming
  • 开始时间 开始时间
S

sanming

Unregistered / Unconfirmed
GUEST, unregistred user!
在李维的著作《Delphi 5。X分布式多层应用系统篇》第三章中讲述了
处理大型数据集的技巧(P3-33)。我因不会用IB,便改用了DBDEMOS数
据库(animals.dbf),在调试示例的时候发现了下列问题:
1。调试:DBGrid中已显示全部记录,输入‘B’,按btnlmprove/query按钮,
光标在最后一条记录上。为什么光标不是在我要找的那条记录上呢?
2。李维在书中说在客户端直接调用ClientDataSet的Locate,Lookup搜寻数据
时,Delphi会先把后端数据表数据传送到客户端,然后再进行搜寻。
调试:输入‘B’,按General按钮,DBGRID显示一条记录,再按btnlocate
/query按钮,DBGRID还是只显示一条记录。按李维所说,按btnlocate/
query按钮后应显示全部数据呀。why?
 
不清楚。。。
 
没有大富来解答这个问题吗?谁看过李维的书,快来看看啊!
附代码:
procedure TForm2.FormActivate(Sender: TObject);
begin
DCOM1.Connected := True;
ClientDataSet1.Active := True;
bFunc := True;
edtRecNo.Text := IntToStr(ClientDataSet1.RecordCount);
end;

procedure TForm2.FormDeactivate(Sender: TObject);
begin
ClientDataSet1.Active := False;
DCOM1.Connected := False;
end;

procedure TForm2.btnLocateClick(Sender: TObject);
begin
bFunc := True;
end;

procedure TForm2.btnImprovedClick(Sender: TObject);
begin
bFunc := False;
end;

procedure TForm2.btnQueryClick(Sender: TObject);
var
lStart, lEnd : Integer;
lCurr, lOffset : Integer;
procedure CheckData;
var
iCount : Integer;
begin
cdsTemp.Data := ClientDataSet1.Data;
while not (ClientDataSet2.Eof)do
begin
if (cdsTemp.Locate('NAME', ClientDataSet2.FieldByName('NAME').Value, []) ) then
ClientDataSet2.Delete
else
ClientDataSet2.Next;
end;
ClientDataSet2.MergeChangeLog;
end;
begin
lStart := GetTickCount;
if (bFunc) then
begin
ClientDataSet1.Locate('NAME', edtSearchField.Text, [loPartialKey, loCaseInsensitive]);
// ClientDataSet1.Open;
edtRecNo.Text := IntToStr(ClientDataSet1.RecordCount);
end
else
begin
ClientDataSet2.Close;
ClientDataSet2.CommandText := 'select * from animals where NAME like ''' + edtSearchField.Text + '%''';

ClientDataSet2.Open;
lCurr := ClientDataSet1.RecNo;
lOffset := ClientDataSet1.RecordCount - lCurr + 1;
CheckData;
ClientDataSet1.AppendData(ClientDataSet2.Data, False);
ClientDataSet1.MoveBy(lOffset);
end;
lEnd := GetTickCount;
edtRecNo.Text := IntToStr(ClientDataSet1.RecordCount);
edtTime.Text := FloatToStr((lEnd - lStart) / 1000.0) + '?';
end;

procedure TForm2.btnGeneralClick(Sender: TObject);
var
lStart, lEnd : Integer;
begin
lStart := GetTickCount;
ClientDataSet3.Close;
if (edtSearchField.Text = '*') then
begin
ClientDataSet1.Active := False;
ClientDataSet1.Active := True;
end
else
begin
ClientDataSet3.CommandText := 'select * from animals where NAME like ' + '''' + edtSearchField.Text + '%''';
ClientDataSet3.Open;
if (ClientDataSet3.RecordCount > 0) then
ClientDataSet1.Data := ClientDataSet3.Data;
end;
lEnd := GetTickCount;
edtRecNo.Text := IntToStr(ClientDataSet1.RecordCount);
edtTime.Text := FloatToStr((lEnd - lStart) / 1000.0) + '?';
end;

end.
 
太长了!
 
很失望啊!怎么还没人来回答我的问题呀?
 
to chenlili
斑竹,你是否能为我解答这个问题呢?麻烦你了!@
 
问题1:
把 ClientDataSet1.AppendData(ClientDataSet2.Data, False);
ClientDataSet1.MoveBy(lOffset);两条语句换为
if ClientDataSet2.RecordCount>0 then
begin
ClientDataSet1.AppendData(ClientDataSet2.Data, False);
if lOffset = 1 then
ClientDataSet1.next
else
ClientDataSet1.MoveBy(-lOffset);
end;
其他还要再改一点。
问题2:
locate方法并没有从后端数据库把数据全部下载下来再搜索;
而是在本地dataset已有数据的基础上进行搜索。
因此如果要用locate方法进行搜索,必须把后端数据库表数据全部下载下来再搜索
也不知是李维写错了,还是你理解错了?
 
问题2:同意modernman的看法;
locate本身不会自动下载后端数据库表数据,它只会利用当前客户端的数据进行查询,
我认为李维的思想是在调用locate方法之前,要注意下载到客户端的数据不应是整个
数据集,数据量大的时候,整个数据集的传输当然要造成系统的沉重负担.
解决办法是先用clientdataset.commandtext下载一个适合客户的子数据集,缩小数据
传输量.

 
To modernman
谢谢你!
第二个问题是我理解有错误。
第一个问题我按你说的修改了,但cursor总是在第一条记录上,仍然没有指向要找的记录上
(ClientDataSet已存在要找的数据时)。
你说还要修改一点,但不知如何修改,请细加说明。
 
在语句lCurr := ClientDataSet1.RecNo;的前面加上下面语句
ClientDataSet1.First;
if not ClientDataSet2.Eof then
begin
while (ClientDataSet1.FieldByName('name').asstring <> ClientDataSet2.FieldByName('name').AsString)
and (not Clientdataset1.eof)do
begin
ClientDataSet1.Next;
end;
end;
 
对第二个问题你们的理解都错了,
李维的意思是:用LOCATE时客户端会把服务端QUERY中的内容全下到客户端。
如:ClientDataSet1通过Provider连到服务端的Query,Query中已用sql语句查到500条记录,
而客户端设置ClientDataSet的PacketRecordes为20,FatchOnDemond为True,自动分段下载,
首次先从服务端取出了20条,向下滚动时会再自动取20条,ClientDataSet并不一次取完全部
500条记录而只有40条。
但当用ClientDataSet.locate查找某记录时,此时ClientDataSet会一次性将服务端的500条
记录全部取到ClientDataSet中,然后再在ClientDataSet中查找。
因此当服务端的Query中已有的数据集较大时,客户端要在此结果集中Locate某条记录要格外注意。
 
to quqs
调用locate时请验证clientdataset.beforegetrecord是否触发!
 
to modernman
谢谢你的解答!
to guqs
呵呵,我觉得你的理解跟我当初理解的一样。
我按shsshashssha所说的验证了一下,Locate 时clientdataset.beforegetrecord并
没有触发!
那应该是李维写错了吧!?
另:PacketRecordes为20,FatchOnDemond为True时,并没有自动分段下载,而是把Query
中的数据全部下载了。PacketRecordes为20,FatchOnDemond为False时,先下载了20条,
但向下滚动时不会再自动取20条,而需要调用GetNextPacket方法。
 
to sanming
这不单单是我的理解,
而是我在实际使用时,PacketRecordes为20,FatchOnDemond为True时,的确是分段下载,显示
ClientDataSet1.RecordeCount为20,当使用LOCATE后,一次全部下载,clientdataset1
显示为500条。你的因为打开时已经全部下载下来数据,当然再用Locate时不会触发
clientdataset.beforegetrecord。而且若设置FatchOnDemond为False,采用手工
GetNextPacket,当然也不会触发clientdataset.beforegetrecord。你得先解决自动分段下
载的问题。当然应该可以自动分段下载,不然设置FatchOnDemond=true还有什么用?
to modernman
我验证到clientdataset.beforegetrecord的确触发!
to all:
不知你们怎么搞的。
用个最简单的作实验。
服务端放一个query 设置好 SQL语句为;'select * Form Tablename',假设Query可得到 500 条
记录,设置Active=true,连好DataSetProvider。
客户端放置 ClientDataSet1,连好Connection,Providername,设置好PacketRecordes为20,
FatchOnDemond为True,设置active=true.
初次运行,显示ClientDataSet1.RecordCount为20,向下滚动,
显示ClientDataSet1.RecordCount为40,执行Locate,
显示ClientDataSet1.RecordCount为500.
可以自动分段下载,可以触发clientdataset.beforegetrecord。
不知你们的是怎么回事?你们在什么地方出了问题,怎么会一次下载下来?既然已经一次下载
下来了,用Locate当然不会触发clientdataset.beforegetrecord。
你们再这样试试。
 
那本书 我以前看过的,有几个地方好像有点小bug,或是印刷错误,但自己改一下
并无大碍。这个例子好像能行的
 
to guqs:
诚如你所说,PacketRecordes为20,FatchOnDemond为True时,的确是自动分段下载,
每次按packetRecords的数值下载。但只要服务端数据没有取完,客户端就会不断触发clientdataset.beforegerecord事件,最终clietdataset.recordcount总会等于服务端数据量,这与调用locate似乎没有关系。
为了弄清这个问题:您可以回答下列问题吗?
1.调用locate的时机。
2.若在clientdataset.beforegerecord事件调用locate,请贴出该事件的代码。(
需弄清在某次申请数据包时,调用locate前后的clientdataset.recordcount)
谢谢!!!
 
Locate 会先在本地纪录集中查找,若找到,则不会到服务端下载,若没找到,且
ClientDataset的FatchOnDemond为True时,才下载所有服务端的数据到客户端
 
to sjt:
你的理解错误,Locate查找时,即使在本地找到,同样会到服务器端下载,因为它要求在
全集中查找。你试试便知。
to shsshashssh:
也不知你是赞同我在请教,还是否定我在反问我。
1、“但只要服务端数据没有取完,客户端就会不断触发clientdataset.beforegerecord事件”
实际上当使用自动分段下载时,只有客户端提出请求,例如下滚、Locate时,才会下载全部,
否则不会触发触发clientdataset.beforegerecord。用户下滚看时下载需要的部分,不看的
部分不会下载,不管取没取完。
2、“调用locate的时机”
当你需要时就可以,不过要谨慎使用,要么首先Query限制数据集小,可直接使用,
全部下载也无妨。要么采用技巧,李维的例子便是一种。
或者采用更简单的方式,先设置ProviderName为空,断开与Provider的连接,于是用Locate
时由于没有Provider,因此不会下载数据,只能在本地查找,如果没找到,就用另一个
ClientDataset2去查找单条记录,然后用AppandData、mergeChangeLog合到ClientDataset1。
3、没必要在beforegerecord事件调用locate。
 
to guqs:
我已经尽量按您的思路去做了,但是我做出的结果都是客户端自动按数据包多次下载,
在过程中,我无法控制数据包,所以,它将数据全部下载到客户端了。
locate必须在clientdataset.active=true时才能进行检索。
我想知道您是如何实现如下情况:
FatchOnDemond为True,设置active=true.
初次运行,显示ClientDataSet1.RecordCount为20,向下滚动,
显示ClientDataSet1.RecordCount为40,
 
to shsshashssha:
我不清楚你现在出的问题,是不是你在不用Locate时也会下载全部,无法实现自动分段下载。
在我的使用过程中,只要你设置好了FatchOnDemond为True,PacketRecordes为20,当
ClientDataSet.active=true时,就会实现自动分段下载,只下一次,FatchOnDemond就是干
这个的,不需要额外写任何代码。
如果你不能实现这一点,总会一次全部下来的话,一定是有其它代码干扰了。
照我上面帖子里举的方法,做个最简单的例子,什么其他功能都不要加,客户端只要用
ClientDataSet和DataSource和DBGride。多试一下。
若还不行,把你的做法详细讲出来,看是什么问题。
 
后退
顶部