我刚开始学delphi 才看了一本《案例实训》所以还不是很明白
前两天单位给让我做个小程序 大概内容如下
连接到本地数据库,从数据库中读取passinfo表的内容,并且发送给总服务器
注意的是 1.要考虑发送的时候失败怎么办
2.发送信息的时候不能重复发送
3.考虑时间间隔问题(比如:每隔10S上传100条数据)
我现在做到的是,可以将表中的数据读取,并且发送出去。我想要问的是发送信息的时候不能重复发送怎么做?我的思路是将每条信息的唯一标识passid作为一个循环,传送完前N条的时候,记录N的值,下次传送的时候只传送passid>N的数据,但是我就是写不出来,希望各位提点一下,如果能有代码当然最好。
还有剩下这三点注意的各位有什么想法希望能都来讨论一下,让我也借鉴一下,或者有别的注意的地方,大家也谈谈哈,小妹在这里谢谢大家了。
前两天单位给让我做个小程序 大概内容如下
连接到本地数据库,从数据库中读取passinfo表的内容,并且发送给总服务器
注意的是 1.要考虑发送的时候失败怎么办
2.发送信息的时候不能重复发送
3.考虑时间间隔问题(比如:每隔10S上传100条数据)
我现在做到的是,可以将表中的数据读取,并且发送出去。我想要问的是发送信息的时候不能重复发送怎么做?我的思路是将每条信息的唯一标识passid作为一个循环,传送完前N条的时候,记录N的值,下次传送的时候只传送passid>N的数据,但是我就是写不出来,希望各位提点一下,如果能有代码当然最好。
还有剩下这三点注意的各位有什么想法希望能都来讨论一下,让我也借鉴一下,或者有别的注意的地方,大家也谈谈哈,小妹在这里谢谢大家了。
看策略了,最保险的是不提交,下一个10秒再提交,如果你用的是applyupdate(),可以在参数里指定出错的个数。
2.发送信息的时候不能重复发送
数据库里如果应该有主键吧,通过主键判定是否重复是一个好的方法,如果没有主键的话,能否将数据库修改一下,设置主键,通过主键的判断可以避免重复记录提交的。
3.考虑时间间隔问题(比如:每隔10S上传100条数据)
这个用Timer里的事件,设置timer的间隔时间即可吧,具体逻辑看你实现什么了
如果没这多要求就用过全局变量就可以了。
主要是我想知道
每隔一段时间发送一定量的数据 比如说每隔10秒发送10条数据 发完1-10条数据,接着发11-20条数据 而不是再从1开始 发时间问题就用timer控件了 那剩下的隔段时间发10条数据是不是就是用循环做就可以了?如果是的话是不是两层循环?外层循环应该是什么?我自己刚才做一下
for i:=0 to n do
begin
for j:=i*m+1 to i*m+m do
begin
write(j);
end;
end;这里面n是数据的总条数 m是一次发送多少数据 j就当做是数据问题是n这个数据很大 具体是多少只有查数据库了 可以在数据库中查出来写在这里么?或者用个很大的数代替(这样就不精确) 或者还有没有别的办法?
eg:with DataSet do
begin
Filtered := false;
Filter := 'passid>N';
Filtered := True;
First;
while Eof do
begin
... //向主数据库插入数据
Next;
end;
end;
begin
for j:=i*m+1 to i*m+m do
begin
write(j);
end;
sleep(10000);
end;
Timer1
取出相应数据,发送出去,失败的想办法加入全局列表中。
Timer2
检查全局列表,有失败的发送。各干各的事,互不影响。注意两个Timer的时间间隔。
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, DB, DBClient, ExtCtrls, ADODB;type
TForm1 = class(TForm)
Timer1: TTimer;
ADOConnection1: TADOConnection;
ADOTable1: TADOTable;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
updateid: Integer;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
begin
timer1.Enabled := false;
timer1.Interval := 10000;
timer1.OnTimer := Timer1Timer;
updateid := 0;
end;procedure TForm1.Timer1Timer(Sender: TObject);
var
I: Integer;
begin
timer1.Enabled := false;
try
with ADOTable1 do
begin
Filtered := false;
Filter := 'passid>' + inttostr(updateid);
Filtered := True;
First;
I := 0;
while not Eof do
begin
//执行插入,记录最后的插入passid到updteid中
inc(I);
if I >= 10 then
break;
Next;
end;
end;
finally
timer1.Enabled := true;
end;
end;end.
在你的程序里连接本地的passinfo表,然后记录你每次上传的主键值,可以写在配置文件里,按你的提示是记录passid字段的值,然后利用一个TQuery类型的数据集,在他的SQL属性里每次写select * from passinfo where passid > 你记录的值,获得数据集后传100条,就传前面的100条了,只用一个循环就可以实现了。举例来讲是这样的,第一次初始化时候passid=0,那么select * from passinfo where passid>0 传100条后,记录passid为100,然后再传的时候select * from passinfo where passid>passid(值是100),就避免重复传递了,如果passid不是主键,没有索引,可以利用order by子句。循环就是for i:=0 to n do 执行你的上传,插入的语句,可以在timer里面实现,当然Interval属性设置成10000
谢谢各位了 大家都是大好人O(∩_∩)O~
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, DB, ADODB;type
TForm1 = class(TForm)
ADOQuery1: TADOQuery;
ADOQuery2: TADOQuery;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
inilist: TStringList;
inifile: string;
updateid: Integer;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
begin
inifile := ChangeFileExt(Application.ExeName, '.ini');
inilist := TStringList.Create;
try
if fileexists(inifile) then
begin
inilist.LoadFromFile(inifile);
updateid := strtoint(inilist[0]);
end else begin
updateid := 0;
end;
except
messagebox(Application.Handle, '读取ini失败', '错误', MB_ICONERROR or MB_OK);
exit;
end;
timer1.Enabled := false;
//设置10秒间隔
timer1.Interval := 10000;
timer1.OnTimer := Timer1Timer;
timer1.Enabled := true;
ADOQuery1.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\本地数据库.mdb;Persist Security Info=False';
ADOQuery2.ConnectionString := 'Provider=MSDAORA.1;Password=123456;User ID=bill;Data Source=server;Persist Security Info=True';
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
if inilist <> nil then
inilist.Free;
end;procedure TForm1.Timer1Timer(Sender: TObject);
var
I: Integer;
begin
timer1.Enabled := false;
try
with ADOQuery1 do
begin
SQL.Clear;
SQL.Add('select * from passinfo where passid>'+inttostr(updateid)+' order by passid');
Active := True;
First;
I := 0;
while not Eof do
begin
//更新数据
//
Inc(I);
//插入10条后终止
if I >= 10 then
Break;
Next;
end;
updateid := updateid + I;
Active := False;
end;
try
inilist.Clear;
inilist.Append(inttostr(updateid));
inilist.SaveToFile(inifile);
except
messagebox(Application.Handle, '保存passid失败', '错误', MB_ICONERROR or MB_OK);
end;
finally
timer1.Enabled := true;
end;
end;end.
另外还给点建议:不要用循环实现延时,除非是在线程里。Windows是基于消息驱动的系统。用程序在执行循环时不能处理系统给的消息,除非循环不在主线程。这时程序外在表象好像是死锁了一样。
begin
//更新数据 //
Inc(I);
//插入10条后终止
if I >= 10 then
Break;
Next;
end;
updateid := updateid + I;应该是如下代码
while not Eof do
begin
//更新数据 //
updateid := FieldByName('passid').AsInteger;
Inc(I);
//插入10条后终止
if I >= 10 then
Break;
Next;
end;
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls,FastChannelServices;type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
ResponseMemo: TMemo;
DataSource1: TDataSource;
passinfoDBGrid: TDBGrid;
SendButton: TButton;
CloseButton: TButton;
TimeEdit: TEdit;
SendEdit: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
SendTimer: TTimer;
maxvalueDBGrid2: TDBGrid;
DataSource2: TDataSource;
ADOQuery2: TADOQuery;
procedure SendButtonClick(Sender: TObject);
procedure CloseButtonClick(Sender: TObject);
procedure SendTimerTimer(Sender: TObject);
private
{ Private declarations }
IDmax:string;
function sendSQL(n:string):string;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}
function TForm1.sendSQL(n:string):string;
var
passid,passtime,passcode,deviceid,cardnum,userid,passstatus,resultcode,response,responsecode:string;
Time: TDateTime;
begin
passid:=AdoQuery1.FieldByName('passid').AsString; //获得passid
while length(passid) <18 do
begin
passid:= '0'+passid;
end; Time:=AdoQuery1.fieldbyname('passtime').Value; //获得passtime
Passtime:=FormatdateTime('yyyymmddhhnnss',Time); PassCode:=AdoQuery1.fieldbyname('sysid').asstring; //获得passcode
passcode:='0'+passcode; deviceID:=AdoQuery1.fieldbyname('Alleyid').asstring; //获得deviceid cardNum:=AdoQuery1.fieldbyname('cardid').asstring; //获得cardnum userID:=AdoQuery1.fieldbyname('userid').asstring; //获得userid passstatus:=AdoQuery1.fieldbyname('passstatus').asstring; //获得passstatus
resultcode:=PassId+Passtime+PassCode+'<root><deviceID >'+deviceID+'</deviceID ><cardNum>'+cardNum+'</ cardNum ><userID>'+userID+'</userID><actionTime >'+Passtime+'</actionTime><status>'+passstatus+'< /status ></root>'; memo1ResponseMemo.Lines.Add(passid);
memo1ResponseMemo.Lines.Add(GetFastChannelServicesSoap.FastChannelInterface(resultcode)); //发送数据并在memo里显示结果 response:= GetFastChannelServicesSoap.FastChannelInterface(resultcode);
{
responseCode:=copy(response,21,2); //对返回的报文检查
if responseCode ='02' then //如果报文返回值为02
begin //输出报文信息
memo1ResponseMemo.Lines.Add(GetFastChannelServicesSoap.FastChannelInterface(resultcode));
if responseCode ='01' then
begin
//showmessage('发送成功!');
adoquery1.next;
end
else if messagedlg('网络连接出错,请检查网络',mtconfirmation,[mbyes,mbno],0)=6 then
close;
end;
}
adoquery1.next;
end;procedure TForm1.SendButtonClick(Sender: TObject);
begin //IDmax为passid的最大值,初始值为0
SendTimer.enabled:=true; //Timer开始
end;procedure TForm1.CloseButtonClick(Sender: TObject);
begin
close;
end;procedure TForm1.SendTimerTimer(Sender: TObject);
var
sendNo,response,responsecode,a:string;
begin
sendNo:=SendEdit.text; //输入一次发送的条数
SendTimer.interval:= strtoint(TimeEdit.Text)*1000; //输入时间间隔 ADOquery2.close;
ADOquery2.SQL.clear;
ADOQuery2.SQL.add('select max(passid) as maxvalue from passidrecord');
ADOQuery2.Open;
IDmax:=ADOQuery2.FieldByName('maxvalue').AsString; AdoQuery1.Close; //关闭原来的查询
AdoQuery1.SQL.Clear; //清空原来的SQL命令
AdoQuery1.SQL.text:='select top '+ sendNo+'*from passinfo where passid >'+IDmax+' order by passid'; //用SQL命令 查询passinfo表的前sendno条数据
AdoQuery1.Open; while not adoquery1.eof do //执行sendSQL函数
begin
sendSQL(sendNo);
end;
a:=AdoQuery1.FieldByName('passid').AsString; //记录前一次发送的最大passid值 ADOquery2.close;
ADOquery2.SQL.clear;
ADOQuery2.SQL.add('insert into passidrecord(passid) values('+a+')');
ADOQuery2.ExecSQL;
end;
end.
这是最后完成基本要求的代码 大家看看哪里 还应该改进的 谢谢各位了
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, StdCtrls, ComCtrls, ExtCtrls, DB, ADODB,SYSINI;type
TFrmSCQTSJ = class(TForm)
Panel4: TPanel;
LblMessage: TLabel;
BtnRetry: TButton;
Panel3: TPanel;
ProgressBar1: TProgressBar;
GroupBox9: TGroupBox;
BtnStart: TButton;
BtnClose: TButton;
PanelFCSJ: TPanel;
GroupBox5: TGroupBox;
DBGrid03: TDBGrid;
DataSource1: TDataSource;
ADOQuery1: TADOQuery;
ADOConnection1: TADOConnection;
ADOQuery2: TADOQuery; procedure ShowData;
procedure BtnStartClick(Sender: TObject);
procedure BtnCloseClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure BtnRetryClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmSCQTSJ: TFrmSCQTSJ;
implementation
uses FrmMain1;//adoquery1.conn.....是连到这个单元里的ADOConnection。
{$R *.dfm}
procedure TFrmSCQTSJ.ShowData; //选择要传的数据。。应该如何选数据。上面已经说了。
begin
with ADOQuery1 do
begin
close;
sql.Clear ;
sql.Add('SELECT a.*, b.日期 AS 对帐截止日期 FROM ( SELECT 所在地, 债务人, SUM(欠款金额 - 实收金额) AS 欠款 FROM 欠条表 ');
sql.Add(' WHERE (有效 = 1) AND (已结清 = 0) GROUP BY 债务人, 所在地 ) a LEFT OUTER JOIN ');
sql.Add(' ( SELECT 所在地, 债务人, max(对帐截止日期) AS 日期 FROM 欠条表 ');
sql.Add(' WHERE (有效 = 1) AND (已结清 = 0) GROUP BY 债务人, 所在地 ) b ON b.所在地 = a.所在地 AND b.债务人 = a.债务人 ');
open;
end;
end;
procedure TFrmSCQTSJ.BtnStartClick(Sender: TObject);
begin
if MessageDlg('您确定 上传欠条数据 吗?',mtConfirmation,[mbYes,mbNo],0)=mrNo then
exit;
ProgressBar1.Max:=ADOQuery1.RecordCount;
ProgressBar1.Step:=1;
ADOQuery1.First ;
while not ADOQuery1.Eof do
begin
ADOQuery2.close;
ADOQuery2.sql.Clear;
ADOQuery2.sql.Add('select top 1 * from 欠条汇总表 where 古镇已下载=1 and 总部已下载=1 ');
ADOQuery2.Open;
if ADOQuery2.Eof then
ADOQuery2.Append
else
ADOQuery2.edit;
//用事物处理记录失败的那条。、、在回滚那里记录。
ADOQuery2.FieldByName('债务人').AsString:=ADOQuery1.FieldByName('债务人').AsString;//key
ADOQuery2.FieldByName('所在地').AsString:=_SYS_SFD;
ADOQuery2.FieldByName('欠款总金额').AsString:=ADOQuery1.FieldByName('欠款').AsString;
ADOQuery2.FieldByName('对帐截止日期').AsString:=ADOQuery1.FieldByName('对帐截止日期').AsString;
ADOQuery2.FieldByName('总部已下载').AsBoolean :=false;
ADOQuery2.FieldByName('古镇已下载').AsBoolean :=false;
ADOQuery2.Post ; ADOQuery1.Next;
ProgressBar1.StepIt;
end;
ShowMessage('上传完毕!');
end;procedure TFrmSCQTSJ.BtnCloseClick(Sender: TObject);
begin
close;
end;procedure TFrmSCQTSJ.FormShow(Sender: TObject);
begin
try
ADOConnection1.Connected:=False;
ADOConnection1.ConnectionString:=IP_Str;//数据连接字段。
ADOConnection1.LoginPrompt:=false;
ADOConnection1.Connected:=true;
LblMessage.Caption:='数据库连接成功!';
BtnReTry.Enabled:=False;
except
LblMessage.Caption:='数据库连接失败,请确认网络连接无误后,按“重新连接”按钮!';
BtnStart.Enabled:=False;
BtnReTry.Enabled:=True;
end;
ShowData;
end;procedure TFrmSCQTSJ.BtnRetryClick(Sender: TObject);
begin
try
ADOConnection1.Connected:=False;
ADOConnection1.ConnectionString:=IP_Str;
ADOConnection1.LoginPrompt:=false;
ADOConnection1.Connected:=true;
LblMessage.Caption:='数据库连接成功!';
BtnReTry.Enabled:=False;
except
LblMessage.Caption:='数据库连接失败,请确认网络连接无误后,按“重新连接”按钮!';
BtnStart.Enabled:=False;
BtnReTry.Enabled:=True;
end;
ShowData;
end;end.
事物处理。我以前问的。大家回答的很详细。异常处理
try
⑴ //利用事物来抛出异常(也不知道我说的对不,向你师父求证。)
except // 1 执行不成功,才会再执行 2 。
⑵
end 对于用数组: 程序关了。就没了
比如INI文件来存储是不错的。。
(还有TSTINGLIST 读写TXT 文本文件。)
其实处理失败有很简单的方法。遇到失败记录,记下当前记录id,终止此次发送等下个10秒,从当前记录在开始发送。
我给的那个例子里在//更新数据 那里加个发送失败触发异常就可以了,原先我以为发送失败会出现异常所没有提。这样当前发送过程终止,等下次timer触发。因为每次发送都把passid记录在updateid中了,下次发送的时候就是从记录的updateid开始的。不过这个例子有个缺点就是没有把每次发送的passid记录的文件中,如果出现程序异常终止。就可能会发以前发送的一部分以前发送过的记录。要改进可以需要把记录文件这段放在发送循环内。
你也可以简单的改进一下,在sendSQL(sendNo);前后加上try except异常处理:
try
sendSQL(sendNo);
except
end;
把
else if messagedlg('网络连接出错,请检查网络',mtconfirmation,[mbyes,mbno],0)=6 then
close;
end;
中的close改成abort如下:
else if messagedlg('网络连接出错,请检查网络',mtconfirmation,[mbyes,mbno],0)=6 then
abort;
end;
close是关闭程序,abort是抛出异常终止当前执行。
还有你的程序写的有点乱,像
while not eof do
begin
...
next;
end;
这种比较常见结构最好不要吧next写入到另外一个函数里。
结构你还可以在优化一下,有些操作不必要。还有你看别人的代码都有格式排版,要知道对程序员来说看代码的时间会超过写代码的时间。以后你还要维护自己的代码,格式很乱会加重你日后维护的负担。