如何从客户端一次性地向远程的服务器导入大量的数据?(分布式多层应用)(200分)

  • 主题发起人 主题发起人 caojun77
  • 开始时间 开始时间
C

caojun77

Unregistered / Unconfirmed
GUEST, unregistred user!
目前这套系统的结构是:应用程序服务器和数据库服务器(MS SQL Server 7.0)都在局域网内的同一台服务器上;而客户端则在局域网内的另外一台机器上。
——>对于应用程序服务器来说,其主要构成就是一个Remote Data Module,在这个远程数据模块中,放置有下述三个构件:TADOConnection,TADOQuery和TDataSetProvider。
本来是想利用客户端的TClientDataSet的CommandText属性,这样比较方便,但是CommandText的类型属于String;而从客户端传来的每一条记录的SQL语句实在是太长了,并且该SQL语句的类型属于TStrings,
譬如:有这样的一条插入记录的SQL语句:
Insert into House_Sale(HouseID,UID,PropertyName,RentSale,Prince,City,District,Address,Mright,
Purpose,HouseType,BuildingType,Floor,HouseYear,BuildingArea,LivingArea,Direction,Telephone,
ContactPerson,UserCode,Email,BuyReason,LimitDate,Price,PriceType,PropertyFee,PropertyFeeType,
Taxes,Gas,Water,Electricity,Kitchen,Toilators,Equipment,Bus,SaleDesp,IsShow,Fitment,Parklod,
BrokerUserName)
values('HID2000122100155','20001221',NULL,'1','上海','上海','徐汇','(编号为:0100520003)罗秀路1095弄',
'售后','住宅','二室','多层','1',0,0,'11.9+8.29',NULL,'64223038','汇成置换','MA000928HC',NULL,NULL,
0,10,'万元/套',0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'0',NULL,NULL,NULL)
为了解决这个问题,我采用了在应用程序服务器的TDataSetProvider中加入OnDataRequest事件处理函数,这样就会和客户端的TClientDataSet的DataRequest相呼应。OnDataRequest事件处理函数如下:
function TRDM_mtdata.dspHouse_SaleDataRequest(Sender: TObject;
Input: OleVariant): OleVariant;
begin
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Text:=Input;
Input:=null;
ADOQuery1.ExecSQL;
end;

——>对于客户端来说,我的想法是这样的:先将客户端本地的数据源从Access数据库中取出并显示在客户端的DBGrid中,然后通过远程的应用程序服务器向MS SQL Server 7.0上的一个表导入大约300条这样的数据。
在这里我用了下列两组控件:
一、TDatabase,TTable,TDataSource和TDBGrid,使用这组控件的目的是:将客户端本地的数据源从Access数据库中取出并显示在客户端的DBGrid中。
二、TSocketConnection、TClientDataSet和TQuery,使用这组控件的目的是:通过TQuery的SQL属性为本地的每一条记录构造一条SQL插入语句,并以参数的形式将其传递给TClientDataSet的DataRequest方法,最终通过
TSocketConnection和远程的应用程序服务器的TDataSetProvider进行通讯。
导入数据的具体源代码如下:
procedure Tfrm_House_Sale.BitBtn3Click(Sender: TObject);
var iField,i:Integer;
desttbl,sql_House_Sale:string;
begin
i:=1;
with Table1do
begin
Open;
{打开老系统的表}
desttbl:='House_Sale';
while EOF=FALSEdo
begin
{逐条记录处理}
qryInsert.SQL.Text:='';

qryInsert.sql.Add('Insert into '+desttbl + '(');
for iField:=0 to dbg.FieldCount-1do
begin
qryInsert.sql.add
(dbg.Fields[iField].DisplayLabel);
if iField<>dbg.FieldCount-1 then
qryInsert.sql.add(',');
end;
qryInsert.sql.add(') values(');
for iField:=0 to dbg.FieldCount-1do
begin
{进行数据类型转换}
if dbg.fields[iField].DataType=ftInteger then
qryInsert.sql.add(inttostr
(dbg.fields[iField].asInteger));
if dbg.fields[iField].DataType=ftFloat then
qryInsert.sql.add(floattostr
(dbg.fields[iField].asFloat));

if dbg.fields[iField].DataType=ftString then
begin
if dbg.fields[iField].asString<>'' then
qryInsert.sql.add(''''+dbg.fields
[iField].asString+'''')
else
qryInsert.sql.add('NULL');
end;
if dbg.fields[iField].DataType=ftMemo then
begin
if dbg.fields[iField].asString<>'' then
qryInsert.sql.add(''''+dbg.fields
[iField].asString+'''')
else
qryInsert.sql.add('NULL');
end;
if iField<>dbg.FieldCount-1
then
qryInsert.sql.add(',');
end;
qryInsert.sql.add(')');
with ClientDataSet1do
begin
// Close;
DataRequest(qryInsert.sql.Text);
//Open;
end;

ProgressBar1.Position := i;
i:=i+1;
next;
end;
ShowMessage('转换完毕!');
end;
end;

问题列表:
一、我在本地的机器上(即:MS SQL Server 7.0、应用程序服务器和客户端都存在于同一台机器上),数据导入工作一直正常。但一旦将MS SQL Server 7.0和应用程序服务器放于局域网内的另外一台机器上时,再进行调试,就出错,具体表征如下:
1、完全能成功地将300条记录导入SQL Server中的概率是30%左右;多数情况是,在导入了一部分记录后,客户端便没有任何反应了,最终在任务管理器中出现“没有响应”。
2、我尝试着将数据表中的记录减少,同时在任务管理器中将该进程的优先级人工地设为最高级别——实时,这样的话,成功的可能性较前者要高一些。
3、一般情况下,客户端没有响应后,并没有其它的出错提示信息;但有时会偶尔出现这样的提示信息:
BDE Error $000F,查阅有关资料后,得知:这可通过增加BDE Administrator中的Configuration->System->INI中的SharedMemSize来除错。试过之后,虽然不再出现BDE Error $000F这种出错信息,但能够成功地将数据导入SQL Server中的概率还是很低的。
二、对于我的这种情况,是要对BDE的有关参数进行设置,还是要对上述所使用控件的一些相关属性进行设置?
具体应当怎样进行设置?由于我在应用程序服务器端采用了ADO方式,不知运行有应用程序服务器的那台机器上的BDE是否也要进行相关设置?
三、是不是要对一些有关内存的大小、超时的控制或采用异步、同步的方式来进行设置?具体应当设置哪些?
四、不知上述我所采用的方式是不是本身就存在着不合理性?不知“流的方式”或“Sock/Socket方式”是怎样实现的?
附:我的客户端应用程序占用内存的情况以及系统的一些相关参数:
1、刚进入客户端应用程序时,大约占用2.3M的内存,然后进入正式导入数据的界面时,占用内存9-10M(因为此时要将本地的大量数据读入内存并且显示在DBGrid中),最后开始正式导入数据的工作,内存的占用量会不断地上升,一般当内存的占用量不超过11.5-12M时,完全成功的可能性比较大。
2、BDE Administrator中的Configuration->System->INI中的一些参数如下:
MAXBUFSIZE 2048
MAXFILEHANDLES 48
MEMSIZE 16
MINBUFSIZE 128
SHAREDMEMSIZE 2048

急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!急!!



 
to caojun77
你的应用应该不是在线式的吧?
如果是公文包式的,用了DBE又用ADO不是重复了吗?DELPHI的数据库应用的公文包模式不需要
再用ACCESS来转换,这是概念问题。
如果是数据中心的提供着和使用者的模式:用ADO直接绕过DBE或使用ACCESS-SQL工程项目
能很好的连接。使用DBE就不要用ADO,两者数据包的结构有所不同。所以在CLIENT端的解包
存在问题。
使用那种连接取决于速度的要求。
 
用batchmove不行吗?
 
两个帖子。内容一样。
 
我觉得您最好不要在此类场合使用BDE,因为有BUG。在分布式系统中,我想还是用
ADOExpress比较妥当。
您可以使用MTS来完成此功能啊?或者,您也可以考虑上面哪位师兄的XML。都不错,具体您
可以看看李维的《DELPHI FOR ADO/MTS/COM+》,比较详细。
 
你给BDE开的内存太小了!!
假设你的物理内存有128M,就设置如下:
MAXBUFSIZE 20480
MAXFILEHANDLES 128
MEMSIZE 100 //关键所在
SHAREDMEMSIZE 20480
真弄不懂,你给分倒挺慷慨,BDE内存你就如此吝啬!
 
咦?一样的贴子?
 
推荐使用ADOdataset,
ADOexpress好像有点问题,不推荐
还有就是使用时注意 cachesize 最好设到1000 这是最佳状态(理论上的)
 
如果可能,将这300条语句以一定的分割标志组成一句长string并request.
然后,在应用服务器上解析这长string并分解为300条SQL.
然后再循环调用TADOQuery的ExecSql执行.
我想效果会好的多。如果是,请告知我bicycleor@263.net
 
多人接受答案了。
 
后退
顶部