我刚开始学delphi 才看了一本《案例实训》所以还不是很明白
前两天单位给让我做个小程序 大概内容如下
连接到本地数据库,从数据库中读取passinfo表的内容,并且发送给总服务器
注意的是 1.要考虑发送的时候失败怎么办 
         2.发送信息的时候不能重复发送
         3.考虑时间间隔问题(比如:每隔10S上传100条数据)
我现在做到的是,可以将表中的数据读取,并且发送出去。我想要问的是发送信息的时候不能重复发送怎么做?我的思路是将每条信息的唯一标识passid作为一个循环,传送完前N条的时候,记录N的值,下次传送的时候只传送passid>N的数据,但是我就是写不出来,希望各位提点一下,如果能有代码当然最好。
还有剩下这三点注意的各位有什么想法希望能都来讨论一下,让我也借鉴一下,或者有别的注意的地方,大家也谈谈哈,小妹在这里谢谢大家了。

解决方案 »

  1.   

    1.要考虑发送的时候失败怎么办 ?
       看策略了,最保险的是不提交,下一个10秒再提交,如果你用的是applyupdate(),可以在参数里指定出错的个数。 
      2.发送信息的时候不能重复发送
       数据库里如果应该有主键吧,通过主键判定是否重复是一个好的方法,如果没有主键的话,能否将数据库修改一下,设置主键,通过主键的判断可以避免重复记录提交的。
      3.考虑时间间隔问题(比如:每隔10S上传100条数据)
       这个用Timer里的事件,设置timer的间隔时间即可吧,具体逻辑看你实现什么了
      

  2.   

    还有关于Timer控件,书上就说了 Enabled属性开始是false,结束的时候设为true;但是复杂一点就弄不明白了。我在buttonclick中需要执行一个动作,timer里还需要在执行这个动作么?还是只写timer.enabled:=true;呢 
      

  3.   

    如果想重启什么的还可用,最简单用个Ini或者注册表。
    如果没这多要求就用过全局变量就可以了。
      

  4.   

    我现在在重新说一下 我想要问的内容
    主要是我想知道
    每隔一段时间发送一定量的数据 比如说每隔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这个数据很大 具体是多少只有查数据库了 可以在数据库中查出来写在这里么?或者用个很大的数代替(这样就不精确) 或者还有没有别的办法?
      

  5.   

    读取数据可以使用数据集控件的Filter属性
    eg:with DataSet do
    begin
      Filtered := false;
      Filter := 'passid>N';
      Filtered := True;
      First;
      while Eof do
      begin
        ... //向主数据库插入数据
        Next;
      end;
    end;
      

  6.   

    隔段时间发送不是通过循环实现的,是设置timer控件的Interval属性,单位是毫秒,如果是隔10秒就是10000。如果一定要做循环的话需要在程序中插入sleep(10000),代码就会变为for i:=0 to n do
    begin
      for j:=i*m+1 to i*m+m do
      begin
      write(j);
      end;
      sleep(10000);
    end;
      

  7.   

    其实很简单,用两个Timer实现
    Timer1
    取出相应数据,发送出去,失败的想办法加入全局列表中。
    Timer2
    检查全局列表,有失败的发送。各干各的事,互不影响。注意两个Timer的时间间隔。
      

  8.   

    还在讨论么?给个简单的例子:
    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.
      

  9.   

    我明白你的真正问题了,可以这样解决,你看看行不行。
    在你的程序里连接本地的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
      

  10.   

    忘记了写timer1.Enabled := Ture了,反正大概思路就是这样的。
      

  11.   

    我就是这个意思 但是 记录passid的值应该怎么记录?给个明确代码呗你说的循环里面n代表什么?
      

  12.   

    我去睡觉了 明天上班问问师傅吧 怕挨骂呀%>_<%
    谢谢各位了 大家都是大好人O(∩_∩)O~
      

  13.   

    哎,难得有人还这么刻苦学Delphi啊,现在我都要放弃Delphi了。其实原来的代码就是你说的情况。再具体一点修改给你的代码。如果这还看不懂,你学习的路就会很漫长了。unit Unit1;interfaceuses
      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是基于消息驱动的系统。用程序在执行循环时不能处理系统给的消息,除非循环不在主线程。这时程序外在表象好像是死锁了一样。
      

  14.   

    又写错了,看来好久编程,有些部熟悉了。那个updateid := updateid + I;应该是updateid := Fields[]      while not Eof do
          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;
      

  15.   

    unit Unit1;interfaceuses
      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.
    这是最后完成基本要求的代码 大家看看哪里 还应该改进的  谢谢各位了
      

  16.   

    unit UnitSCQTSJ;interfaceuses
      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.
      

  17.   

    http://topic.csdn.net/u/20090110/15/1e26c3b5-0a39-45a0-a446-d36b1f7d2894.html
    事物处理。我以前问的。大家回答的很详细。异常处理
    try
    ⑴ //利用事物来抛出异常(也不知道我说的对不,向你师父求证。)
    except // 1 执行不成功,才会再执行 2 。

    end 对于用数组: 程序关了。就没了
    比如INI文件来存储是不错的。。
      (还有TSTINGLIST 读写TXT 文本文件。)
      

  18.   

    还在讨论你师父直接告诉你不就完了么
    其实处理失败有很简单的方法。遇到失败记录,记下当前记录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写入到另外一个函数里。
    结构你还可以在优化一下,有些操作不必要。还有你看别人的代码都有格式排版,要知道对程序员来说看代码的时间会超过写代码的时间。以后你还要维护自己的代码,格式很乱会加重你日后维护的负担。