源文摘自:https://cloud.tencent.com/developer/article/1040413 
上周看到了两篇关于DataReader分页的帖子,帖子的观点都是可以是用DataReader来分页,而且效率还不错。 
 
  根据我的分页经历来看,很难理解DataReader分页怎么就快了呢?理论上就不说了,直接用测试说话。 
 
  1、100w条记录,使用SQL语句(max方法)分页,PostBack方式,GridView显示数据。(第一页需要统计总记录数,所以会有点慢)http://demo.naturefw.com/Nonline/QuickPager/200w/GridView.aspx 
 
  2、100w条记录,使用DataReader分页,URL方式,GridView显示数据(没有统计总记录数。没做分页UI,需要直接修改参数查看其他页)。http://demo.naturefw.com/Nonline ... wReader.aspx?page=1  
 
 运行环境: 
 
  SQL Server 2000 。数据库里只有一个表(八个字段),总记录数1181856。数据库文件体积438MB。 
 
  windows Server 2000、asp.net2.0 
 
 测试结果: 
 
  1、SQL语句(max方法),前若干页(不包括第一页),只需要十几毫秒。最后若干页,需要300多毫秒,不超过0.5s。 
 
  2、DataReader分页。很平稳,不管是前面的还是后面的,都是1.5s左右。 
 
  有在线测试(见上面的连接),大家可以自己看看。 
 
 测试说明: 
 
  1、使用的是QuickPager分页控件,由QuickPagerSQL(单独的类库)动态拼接SQL,所以在测试页面下面会出现"拼接SQL用时",这个也是顺便看看拼接SQL的效率。 
 
  另外请注意一下,在显示第一页的时候,会使用count(0)来统计一下总记录数,所以显示第一页的时候,时间会长一点。 
 
  由于是动态拼接的SQL语句,没有使用存储过程,所以每次分页,都可以查看到使用的SQL。 
 
开始时间:2011-05-05 12:02:59  
拼接SQL用时:0秒0毫秒 
加载数据用时:0秒0毫秒 
绑定控件用时:0秒0毫秒 
提取数据使用的SQL语句: 
select top 10 * from Person_User_ViewLog where logid <= (SELECT min(logid ) from (select top 61 logid from Person_User_ViewLog order by logid desc ) as t ) order by logid desc  
 
  2、DataReader分页。由于QuickPager分页控件不支持DataReader分页,所以临时写的代码。没有做分页的UI(偷懒了,大家包涵),采用URL的分页方式,page 表示页号。 
 
 测试分析: 
 
  在这个测试里,DataReader分页慢的原因是数据比较大——100w。如果数据比较少,只有几百条的话,那么差距不会这么大。 
 
  通过测试结果来看,DataReader分页,在速度上完全没有优势。只是在更换数据库上有点优势。不过 QuickPagerSQL 可以产生不同的分页算法来应对不同类型的数据库,DataReader的优势也就不明显了。 
 
 测试代码: 
 
  1、SQL分页 
 
public partial class GridView : Page 
{ 
protected DateTime dtStartTime;    //页面开始时间 
protected DateTime dtSQLTime;      //拼接SQL结束时间 
protected DateTime dtLoadDataTime; //加载数据结束时间 
protected DateTime dtBindDataTime; //绑定控件结束时间 
#region 初始化 
protected override void OnInit(EventArgs e) 
{ 
base.OnInit(e); 
string cnString = ConfigurationManager.AppSettings["cnStringTest"]; 
DataAccessLibrary dal = DALFactory.CreateDAL(cnString, "System.Data.SqlClient"); 
//数据访问函数库的实例,使用基类里定义的。 
Pager1.DAL = dal; 
//设置显示数据的控件 
Pager1.ShowDataControl = GV; 
//定义QuickPager_SQL,设置Page属性 
Pager1.PagerSQL.Page = this; 
//默认是PostBack的分页方式 
dtStartTime = DateTime.Now; 
} 
#endregion 
protected void Page_Load(object sender, EventArgs e) 
{ 
lblMsg.Text = "开始时间:" + dtStartTime.ToString("yyyy-MM-dd HH:mm:ss  
 
"); 
if (!Page.IsPostBack) 
{ 
SetPagerInfo();         //设置表名、字段名等 
} 
} 
#region 给QuickPager_SQL 设置属性,以便拼接SQL 
private void SetPagerInfo() 
{ 
//表名或者视图名,必须设置 
Pager1.PagerSQL.TableName = " Person_User_ViewLog";              //表名或者视图名称 
//一些分页算法必须设置主键。 
Pager1.PagerSQL.TablePKColumn = "LogID";             //主键名称,不支持复合主键 
//排序字段也是必须设置的,否则就无法准确分页 
Pager1.PagerSQL.TableOrderByColumns = "LogID desc "; //排序字段,根据分页算法而定,可以支持多个排序字段 
//默认TableShowColumns是 * ,可以不设置 
//Pager1.PagerSQL.TableShowColumns = "*";    //需要显示的字段 
//没有查询条件,那就不用设置了嘛。 
//Pager1.PagerSQL.TableQuery = "";                      //查询条件 
//默认一页20条记录 
Pager1.PageSize = 10;                                  //一页显示的记录数 
//设置分页方式,默认是Max_TopTop 
Pager1.PagerSQL.SetPagerSQLKind = PagerSQLKind.Max_TopTop  ; 
} 
#endregion 
#region 在拼接SQL和提取数据、自动绑定控件之前触发, 
protected void Pager1_PageChanged(object sender, PageArgs e) 
{ 
//在拼接SQL和提取数据、自动绑定控件之前触发, 
dtSQLTime = DateTime.Now; 
TimeSpan ts = dtSQLTime - dtStartTime; 
lblMsg.Text += string.Format("拼接SQL用时:{0}秒{1}毫秒 
 
", ts.Seconds, ts.Milliseconds); 
} 
#endregion 
#region 加载数据时间 
protected void Pager1_PreGridBind(object sender, PageArgs e) 
{ 
//在提取数据之后,自动绑定控件之前触发 
//计算时间 
dtLoadDataTime = DateTime.Now; 
TimeSpan ts = dtLoadDataTime - dtSQLTime; 
lblMsg.Text += string.Format("加载数据用时:{0}秒{1}毫秒 
 
", ts.Seconds, ts.Milliseconds); 
} 
#endregion 
#region 绑定控件时间 
protected void Pager1_GridBinded(object sender, PageArgs e) 
{ 
//在自动绑定控件之后触发 
//计算时间 
dtBindDataTime = DateTime.Now; 
TimeSpan ts = dtBindDataTime - dtLoadDataTime; 
lblMsg.Text += string.Format("绑定控件用时:{0}秒{1}毫秒 
 
", ts.Seconds, ts.Milliseconds); 
lblMsg.Text += "提取数据使用的SQL语句: 
 
"; 
lblMsg.Text += Pager1.PagerSQL.GetSQLByPageIndex(Pager1.PageIndex);               //测试用 
} 
#endregion 
} 
2、DataReader分页(如果代码有什么问题,还请大家多多指正!) 
 
public partial class GridView : Page 
public partial class GridViewReader : System.Web.UI.Page 
    { 
        protected void Page_Load(object sender, EventArgs e) 
        { 
            bind(); 
        } 
 
        //绑定控件 
        private void bind() 
        { 
            string pageIndex = Request.QueryString["page"]; 
 
            if (!Functions.IsInt(pageIndex )) 
            { 
                Functions.PageRegisterAlert(Page,"参数不正确!"); 
                return; 
            } 
 
            int pIndex = int.Parse(pageIndex); 
 
            if (pIndex < 0) 
            { 
                Functions.PageRegisterAlert(Page, "页号不能小于0 !"); 
                return; 
            } 
 
            DateTime dt1 = DateTime.Now; 
 
            GV.DataSource = LoadData(pIndex); 
 
            DateTime dt2 = DateTime.Now; 
 
            TimeSpan ts = dt2 - dt1; 
            lblMsg.Text = string.Format("用时{0}秒{1}毫秒", ts.Seconds, ts.Milliseconds); 
  
            GV.DataBind(); 
        } 
 
        private DataTable LoadData(int pageIndex) 
        { 
            int pageSize = 10; 
 
            string cnString =  ConfigurationManager.AppSettings["cnStringTest"]; 
 
            string sql = "select * from Person_User_ViewLog order by LogID desc "; 
 
            SqlConnection cn = new SqlConnection(cnString); 
            SqlCommand cm = new SqlCommand(sql,cn); 
 
            cn.Open(); 
            using (SqlDataReader reader = cm.ExecuteReader()) 
            { 
                DataTable dt = new DataTable(); 
                int fieldCount = reader.FieldCount; 
                for (int i = 0; i < fieldCount; i++) 
                { 
                    DataColumn col = new DataColumn(); 
                    col.ColumnName = reader.GetName(i); 
                    col.DataType = reader.GetFieldType(i); 
                    dt.Columns.Add(col); 
                } 
                int totalCount = 0; 
                int first = (pageIndex - 1) * pageSize + 1; 
                int last = pageIndex * pageSize; 
                while (reader.Read()) 
                { 
                    totalCount++; 
                    //退出reader 
                    if (totalCount > last) 
                        break; 
                 
                    if (totalCount >= first && totalCount <= last ) 
                    { 
                        //取值 
                        DataRow r = dt.NewRow(); 
                        for (int i = 0; i < fieldCount; i++) 
                        { 
                            r = reader; 
                        } 
                        dt.Rows.Add(r); 
 
 
                    } 
                } 
                return dt; 
            } 
 
        } 
    } |