捐赠 | 广告 | 注册 | 发布 | 上传 | 关于我们    
  沪ICP备05001939号 DELPHI盒子 | 盒子论坛 | 盒子文章 | 盒子问答悬赏 | 最新更新 | 论坛检索 | 下载中心 | 高级搜索    
  精品专区 | 繁體中文 | 奖励公告栏 | 直通车账号登陆 | 关闭GOOGLE广告 | 临时留言    
盒子资源分类
全部展开 - 全部合拢
使用原生ADO+DataSet内存表进行分页显示(无下载!)
关键字:ADO 分页 DbGrid
来 自:原创
平 台:Win2k/XP/NT,Win2003 下载所需:0 火柴
深浅度:中级 完成时间:2010/7/31
发布者:dcopyboy 发布时间:2010/8/2
编辑器:DELPHI7 语  种:简体中文
分 类:数据库 下载浏览:1503/17877
加入到我的收藏
下载错误报错
登陆以后才能下载
 用户名:
 密 码:
自动登陆(30天有效)
图片如果打不开,说明流量不够了,请稍候下载……
  使用原生ADO+DataSet内存表进行分页显示

   ADO分页显示,通常有2种方式:

     一种是利用原生ADO的页次定位功能,指定PageSize后,用AbsolutePage切换到Page的位置,而后再读取对应页的数据。但这种方式,必须使用本地游标(adUseClient)模式,会将服务上的数据全部下到本地,存在打开速度慢、服务器压力大的缺陷。
     另一种构建复杂的SQL语句(或存储过程),采用SQL的TOP子句获取指定量的数据,效率很高。这种方式的SQL语句是特别改写的,无法做到原汁原味,有些特别复杂的SQL语句难以改写。

   能否有第一种方式不用改写SQL语句而又高效便捷的ADO分页方式呢?

   事实上,ADO 的服务游标下单向数据可以有很高的数据查询效率。

   笔者采用原生ADO的服务游标下单向数据模式进行了分页显示尝试,取得了很好的效果。

   实例:在一个15万的邮编库中取页大小为200的任一页数据,并在DBGRID中显示出来,耗时仅百毫秒。

   Dcopyboy
   Email:dcopyboy@tom.com
   QQ:445235526


{**********
        单元名称:使用原生ADO+DataSet内存表进行分页显示
        创建日期:2010-07-18
        创建者   卢良红 Dcopyboy
        功能:     使用原生ADO+DataSet内存表t进行分页显示
        当前版本:
        Email:dcopyboy@tom.com
        QQ:445235526
**********}
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, ADODB, Grids, DBGrids, StdCtrls, ExtCtrls, ComObj, OleDB, ADOInt;

type
  TForm1 = class(TForm)
    Panel1:TPanel;
    Button1:TButton;
    DBGrid1:TDBGrid;
    DataSource1:TDataSource;
    DadoQ:TADODataSet;
    Button2:TButton;
    Button3:TButton;
    Button4:TButton;
    Label1:TLabel;
    procedure Button1Click(Sender:TObject);
    procedure Button4Click(Sender:TObject);
    procedure Button2Click(Sender:TObject);
    procedure Button3Click(Sender:TObject);
  private
    { Private declarations }
    aPage:Integer;
  public
    { Public declarations }
  end;

var
  Form1:TForm1;

implementation

{$R *.dfm}

//实现分页处理的核心过程

function SavePageToQuery(SQLText:string; Dadoq:TADODataSet; PageSize,
  Page:integer):integer;

  function ADOTypeToFieldType(const ADOType:DataTypeEnum; EnableBCD:Boolean =
    True):TFieldType;
  begin
    case ADOType of
      adEmpty:Result := ftUnknown;
      adTinyInt, adSmallInt:Result := ftSmallint;
      adError, adInteger, adUnsignedInt:Result := ftInteger;
      adBigInt, adUnsignedBigInt:Result := ftLargeInt;
      adUnsignedTinyInt, adUnsignedSmallInt:Result := ftWord;
      adSingle, adDouble:Result := ftFloat;
      adCurrency:Result := ftBCD;
      adBoolean:Result := ftBoolean;
      adDBDate:Result := ftDate;
      adDBTime:Result := ftTime;
      adDate, adDBTimeStamp, adFileTime, adDBFileTime:Result := ftDateTime;
      adChar:Result := ftFixedChar;
      adVarChar:Result := ftString;
      adBSTR, adWChar, adVarWChar:Result := ftWideString;
      adLongVarChar, adLongVarWChar:Result := ftMemo;
      adLongVarBinary:Result := ftBlob;
      adBinary:Result := ftBytes;
      adVarBinary:Result := ftVarBytes;
      adChapter:Result := ftDataSet;
      adPropVariant, adVariant:Result := ftVariant;
      adIUnknown:Result := ftInterface;
      adIDispatch:Result := ftIDispatch;
      adGUID:Result := ftGUID;
      adDecimal, adNumeric, adVarNumeric:
        if EnableBCD then Result := ftBCD
        else Result := ftFloat;
    else
      Result := ftUnknown;
    end;
  end;

  procedure AddFieldDef(F:variant; FieldDefs:TFieldDefs);
  var
    FieldType:TFieldType;
    FieldDef:TFieldDef;
    I:Integer;
    FName:string;
    FSize:Integer;
    FPrecision:Integer;
  begin
    FieldType := ADOTypeToFieldType(F.Type, true);
    if FieldType <> ftUnknown then
    begin
      FSize := 0;
      FPrecision := 0;
      FieldDef := FieldDefs.AddFieldDef;
      with FieldDef do
      begin
        FieldNo := FieldDefs.Count;
        I := 0;
        FName := F.Name;
        while (FName = '') or (FieldDefs.IndexOf(FName) >= 0) do
        begin
          Inc(I);
          if F.Name = '' then
          FName := Format('COLUMN%d', [I]) else { Do not localize }
          FName := Format('%s_%d', [F.Name, I]);
        end;
        Name := FName;
        if (F.Type = adNumeric) and (F.NumericScale = 0) and
          (F.Precision < 10) then
          FieldType := ftInteger;
        case FieldType of
          ftString, ftWideString, ftBytes, ftVarBytes, ftFixedChar:
          FSize := F.DefinedSize;
          ftBCD:
          begin
          FPrecision := F.Precision;
          FSize := ShortInt(F.NumericScale);
          if FSize < 0 then FSize := 4;
          end;
          ftInteger:FSize := 4;
          ftGuid:FSize := 38;
        end;
        if ((adFldRowID and F.Attributes) <> 0) then
          Attributes := Attributes + [faHiddenCol];
        if ((adFldFixed and F.Attributes) <> 0) then
          Attributes := Attributes + [faFixed];
        if (((adFldUpdatable + adFldUnknownUpdatable) and F.Attributes) = 0) or
          (FieldType = ftAutoInc) then
          Attributes := Attributes + [faReadOnly];
        DataType := FieldType;
        Size := FSize;
        Precision := FPrecision;
      end;
    end;
  end;

var
  x, i:word;
  a, RecordCount:integer;
  AConnection, SQuery:variant;
begin
  //有人说,ADO在打开时就会读入全部数据,这似乎不可能哦!
  //笔者采用15万的邮编库,保存到文件中的流大小约5.8M,正常打开
  //用时数毫秒,移动数据指针用时百毫秒,这就是单向数据快的地方
  //为了保证能双向翻页,不的不每次重新打开数据
  AConnection := CreateOleObject('ADODB.Connection');
  AConnection.Open('Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=AYZD;Data Source=.');
  a := GetTickCount;
  SQuery := CreateOleObject('ADODB.RecordSet');
  // SQuery.CursorLocation:=adUseClient; //不能采用本地游标,否则超慢!!
  //下面这几句是为了取得记录数的,不用每次均查吧!!
  {SQuery.open('select count(*) as RecordCount from (' + SQLText + ') as bb',
    AConnection, adOpenForwardOnly, adLockReadOnly, adCmdText);
  RecordCount := SQuery.Fields['RecordCount'].Value;
  SQuery.close;}
  RecordCount:=153726;
  SQuery.open(SQLText, AConnection, adOpenForwardOnly,
    adLockReadOnly, adCmdText);
  if page <= 0 then page := 1;
  if (page - 1) * pagesize > RecordCount then begin
    page := RecordCount div PageSize;
    if page * pagesize < RecordCount then
      inc(page);
  end;
  Result := Page;
  //服务器端游标时下列2句不能用
  {SQuery.PageSize := PageSize;
  SQuery.AbsolutePage := Page;}
  SQuery.move((page - 1) * PageSize);

  //下边这段是要讲数据转换到 ADODataSet,以便在DBGRID中直接显示
  //如采用StringGrid则要自己重写
  Dadoq.DisableControls;
  Dadoq.Close;
  Dadoq.FieldDefs.Clear;
  for i := 0 to SQuery.Fields.count - 1 do
    AddFieldDef(SQuery.Fields[I], Dadoq.FieldDefs);
  Dadoq.CreateDataSet;
  Dadoq.Open;
  for x := 0 to PageSize - 1 do begin
    Dadoq.Append;
    for i := 0 to SQuery.fields.count - 1 do begin
      try
        Dadoq.Fields[i].Value := SQuery.Fields[i].Value;
      except
      end;
    end;
    Dadoq.post;
    SQuery.MoveNext;
    if SQuery.Eof then break;
  end;
  Dadoq.EnableControls;
  
  Form1.Label1.Caption := '用时:' + inttostr(GetTickCount - a);
  SQuery.close;
  AConnection.close;
end;

procedure TForm1.Button1Click(Sender:TObject);
begin
  aPage := SavePageToQuery('select * from 邮编', DadoQ, 200, 1);
end;

procedure TForm1.Button4Click(Sender:TObject);
begin
  aPage := SavePageToQuery('select * from 邮编', DadoQ, 200, 999999);
end;

procedure TForm1.Button2Click(Sender:TObject);
begin
  aPage := SavePageToQuery('select * from 邮编', DadoQ, 200, aPage - 1);
end;

procedure TForm1.Button3Click(Sender:TObject);
begin
  aPage := SavePageToQuery('select * from 邮编', DadoQ, 200, aPage + 1);
end;

end.
Google
 
本站原创作品,未经作者许可,严禁任何方式转载;转载作品,如果侵犯了您的权益,请联系我们
龙脉加密锁 15元起 Grid++Report 报表 申请支付@网
 相关文章
没有相关文章
相关评论
共有评论7条 当前显示最后6条评论
ppwhb 2010/8/3 1:06:22
感谢楼主,先学习了。
另外,今天是8.3男人节,预祝所有男同胞们节日快乐~~
kylixxp 2010/8/3 10:11:10
不错!楼主提供了一种思路。
顶了再看。thx!
haoxiongdi 2010/8/3 10:29:19
有账号那么久,很少顶人的,先顶了再说。
sox 2010/8/15 16:56:27
zan!thx!
ken0137 2010/11/10 14:35:48
我是初学者,试了都是不成功,还有就是如果表内记录为空,会报错,
哪位写成了,代码发我一份好不好,ken0137@163.com
谢谢各位大侠了
ken0137 2010/11/10 14:37:43
最好是有详细的备注,请帮助我们这些刚刚入门的小鸟吧
我要发表评论 查看全部评论
 
  DELPHI盒子版权所有 1999-2023 V4.01 粤ICP备10103342号-1 更新RSS列表