使用ADOQuery.LoadFromFile取得数据后对Access数据库进行数据更新,特寻求有效的解决方案,望各位FW不吝赐教为谢。(详情见贴) (200分

  • 主题发起人 主题发起人 YNTW
  • 开始时间 开始时间
Y

YNTW

Unregistered / Unconfirmed
GUEST, unregistred user!
使用ADOQuery.LoadFromFile取得数据后对Access数据库进行数据更新,特寻求有效的解决方案,望各位FW不吝赐教为谢。(详情见贴) (200分)<br />由于只能使用软盘作为数据交换的介质,需使用ADOQuery.SaveToFile保存Access数据库中的某些表
的数据,并利用这些数据去更新另一Access数据库中的结构相同的同名表。使用ADOQuery.LoadFromFile
从数据文件中获得数据后,采用Update+Insert/Append,即有相同记录则Update,无则Insert/Append,
尝试了两种方法,一种方法失败,另一种效率极低,故向各位有经验的FW取经,盼能得到各位FW的相助。
方法一、试图通过ADOQuery.UpdateBatch载入数据,但无法更新到表中,猜测为表中记录已清空,
无对应的键值,且LoadFromFile到的数据状态为未修改,故UpdateBatch方法失败。
方法二、构造SQL语句实现数据更新,在数据量稍大(&gt;100K)后,更新速度极慢,应用程序假死,
效率极低,也是很失败的方法。

重金寻求有效的解决方法,望各位FW不吝赐教为谢。
 
先在本地做一个库,主要操作是对本地库,减少I/O操作所花费的时间、系统资源。
然后用线程或者是Timer来控制定时的存文件到软盘,退出也要保存
 
delphi自带的离线数据包的例程看过没有,你的描述不就是一个典型的离线数据包吗?
ado/briefcase.
 
TO j_shen2000:
实际情况并非如例程中的情况一样,老兄请在使用SaveToFile之后清空该表或由同样条件所选择的记录,
(这个操作等同于在另一台无此数据的机器上进行数据更新的操作),就会明白我所说的UpdateBacth
方法失效的原因了,我并非未尝过就提问题的。
 
其实你的两种方式都是一种方式。
UpdateSQL原理也是使用SQL语句,只是利用了OldValue。
给你提供两个方法:
1、直接数据库(MDB)备份,将相关表存成一个MDB文件。因为我没有找到怎么将表导出的办法,所以用了一个
比较土但是使用的方法,在此说出,不怕大家嘲笑了,a.将原来的Access数据库MDB复制成一个文件,然后Drop掉
不复制的表,最后使用压缩功能,将剩余的那个MDB压缩成只有那几个表的小的MDB。
2、服务端读取这个MDB文件,写入到数据库中。
所说的两种方法,第1步都是一样的,第2步不同,法1,如同你的方法,也是我采用的方法,
逐条导入,判断是否存在等。速度是慢一些,不过还可以忍受。
法2,使用Ado连接多个MDB,直接使用SQL语句插入!我没有实验过,但是理论上可以,如下:
SELECT *
FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'f:/pic.mdb';'Admin';''; ///这里输入光盘的路径
pic) where id=yuangong.id
或者:

不对不同的Database控件,两个database控件DataBaseName分别为db1、db2,则如下:
select a.* from ":db1:table1" a, ":db2:table2" b
where a.MC&lt;&gt;b.MC

都应该可以的,但是上面的第二种办法仅仅局限于理论,没有彻底的实验,你不妨实验一下
然后将心得分享出来。OK!
 
其实关于loadfromfile和savetofile进行数据备份的讨论,DFW上面有不少,前段时间
我也参与过,其中最大的问题在于loadfromfile本身,它的原代码中竟然是先CLOSE当前
连接,再将文件读进缓存,所以好象最终也没有讨论出个所以然来。
现在,大多数人都是两个ADO对导,很少有人会想到用ACCESS本身的导入导出工具,其实
在DELPHI中调用也并不是多难的事,甚至可以实现从ACCESS到ODBC之间的转换,单表20万
条数据3分钟转换完毕。
 
建议将数据保存到MDB中,可在程序中动态建立access数据库,然后用adoquery执行建表
,将数据保存到此表中。
建表方法:
uses ComObj单元

procedure TForm1.Button1Click(Sender: TObject);
var
CreateAccess:OleVariant;
begin
CreateAccess:=CreateOleObject('ADOX.Catalog');
CreateAccess.Create('Provider=Microsoft.Jet.OLEDB.4.0;Data

Source=c:/accessmdb.mdb');
end;

导入数据用这种方法:
qrySource, qryTarget
用qrySource打开临时access中的表,
qryTarget.sql := select * from tablename where id = :id

begin
qrysource.first;
while not qrysource.eof do
begin
qrytarget.close;
qrytarget.parameters.paranbyname('id').value :=

qrysource.fieldbyname('id').value;
qrytarget.open;
if qrytarget.recordcount 0 then continue;
qrytarget.insert;
// qrytarget.fieldbyname('').value := ..........
qrytarget.post;
qrysource.next;
application.processmessage;//使程序还可以响应外界消息。
end;
 
TO ALL:
不使用MDB作为数据交换文件的原因:
1、用户希望能看到备份出来的具体是些什么文件,而不是一个MDB文件;
2、尽管使用了压缩控件来打包,对软盘而言MDB文件还是比较庞大。

TO j_shen2000:
老兄提到的直接调用Access的导入导出工具,偶现在还没弄清楚怎么做,
请老兄指点一下,谢谢。


下面是偶新的尝试,请各位FW继续关注:

出于以上两方面的考虑,还是决定使用一表对应一文件的方法,
又尝试了利用SQL语句导出到文本文件,再利用SQL语句从文本文件恢复,
这里使用了Access中直接导出到外部表(把TXT文件当作一个外部表)的
方法,未获成功。按Access中的书写方法如下:
Sql.Text:='select * into '+外部表路径名+' in "'+外部表所在目录
+'" "TEXT;" from '+Access中的表名+查询条件;
错误为:外部TXT文件不是一个有效的路径

由于时间有限,只好暂时放弃上面这种方法,重新从构造SQL语句入手,
这次采用对每张表构造一次SQL语句(原先是对每记录构造两次SQL语句:
Insert和Update语句各一条,这也是导致数据恢复极慢的原因),该SQL
语句只用于Insert,在Insert之前用同备份时相同的查询条件对Access表
中满足条件的记录进行Delete,取消了Update操作,经过这次修改后速度
有了明显提高,大概对一个机构的数据恢复时间为5分钟(偶的机器很差,
所以觉得5分钟应该算比较快了:P)。
 
用ADO可以实现这个功能的,前几天我还实验了这个功能。完全没有有问题,
其实很简单,我一会儿贴一来
 
其实也可以考虑一下用文本文件。自段之间用“,”分隔,表的名就是文件的名。这样用户
不就能看到了吗?在delphi的demo中有一个文本数据库的例子,有bde的控件可以直接使用。
如果喜欢用ado可以用odbc的文本数据库。导入时用
delete table1 where id in (select id from table2) 先删除后直接插入会快的很多,因为
你在查找相同纪录的花费时间很多。还有,是不是先把数据拷贝到硬盘上再导入,这样io的速度
会好一些。我不知道你的机器配置,不过30k条记录应该在30秒内完成。
 
用这个方法试试,我测试了一下,UPDATE/INSERT 9000条记录。用时不到2分种
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, ADODB, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
ADOConnection1: TADOConnection;
Button2: TButton;
ADOQuery1: TADOQuery;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}
uses comobj;
var
cn ,rs,rs1 : Variant;
List : TStringList;
procedure TForm1.Button1Click(Sender: TObject);
var
p,i,idx :Integer;
begin
cn := CreateOleObject('adodb.connection');

rs := CreateOleObject('adodb.recordset');

rs1 := CreateOleObject('adodb.recordset');


cn.Open(adoConnection1.ConnectionString);
rs1.Open('select * from test',cn);
List := TStringList.Create;
while not rs1.Eof do
begin
List.Add(rs1.Fields[0].Value);
rs1.MoveNext;
end;
rs1.Close;

rs.Open('c:/test.xml');
rs1.Pagesize := 1;
rs1.Open('SELECT * FROM TEST',CN,3,2);
p := 0;
while not rs.Eof do
begin
idx := List.IndexOf(rs.Fields[0].Value);
if idx = -1 then
rs1.Addnew
else
rs1.absolutePage := idx+1;
for i := 1 to rs.Fields.Count - 1 do
rs1.Fields.Value := rs.Fields.Value;
rs1.Update;
rs.MoveNext;
Inc(p);
Button2.Caption := IntToStr(p);
button2.Update;
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
ADOQuery1.SaveToFile('c:/test.xml');
end;
 
以上程序没有优化,如果再优化一下的话应该更快
 
楼上的更我说的思路差不多,不过具体多了。
 
留下MAIL地址。
 
TO ALL:
谢谢各位FW,偶正在试

TO j_shen2000:
tigerr@ynmail.com
 
兄弟,你的邮箱有问题。
 
TO j_shen2000:
不好意思呀,老兄,免费邮箱就是这德性,不知道何时出问题,麻烦老兄重发到tigerwr@yeah.net,谢谢
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
926
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部