100w条记录分页,可以有多快?—— DataReader分页与SQL语句分页的对比测试(在线演示)

  上周看到了两篇关于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/QuickPager/200w/GridViewReader.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[i] = reader[i];
                        }
                        dt.Rows.Add(r);


                    }
                }
                return dt;
            }

        }
    }

QuickPager分页控件的在线演示

QuickPager分页控件的在线帮助文档

QuickPager分页控件源码下载 V2.0.4.5

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

使用jQuery Uploadify在ASP.NET 上传附件

Uploadify是JQuery的一个上传插件,实现的效果非常不错,带进度显示。Uploadify官方网址:http://www.uploadify.com/,...

33090
来自专栏张善友的专栏

使用 SoapUI 测试ASP.NET Web API

我们为不同的目的开发了很多web服务,经过授权的用户就可以访问和使用这些web服务。soapUI 是一个强大的测试web服务的工具,他不仅可以测试SOAP服务,...

26080
来自专栏张善友的专栏

让Response.Redirect页面重定向更有效率

用 Redirect 方法可将浏览器重定向到另一个 URL,而不是将内容发送给用户。 这里有一篇文章介绍使用Redirect《Using Response.Re...

27860
来自专栏张善友的专栏

ASP.Net MVC 5 in Xamarin Studio 5.2

Xamarin Studio 是一个Mono的跨平台 IDE(Integrated Development Environment),支持Wiindow和Mac...

21190
来自专栏张善友的专栏

ASP.NET Identity V2

Microsoft.AspNet.Identity是微软在MVC 5.0中新引入的一种membership框架,和之前ASP.NET传统的membership以...

26780
来自专栏张善友的专栏

介绍.NET 开发必备工具 .NET Portability Analyzer

随着.NET的原来越开放,不仅仅是开源这么简单了,也意味着.NET程序员要关注越来越多的平台,涵盖.NET Mic Framework, Xamarin,Mon...

216100
来自专栏张善友的专栏

使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标

通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器、权...

22680
来自专栏张善友的专栏

使用 OWIN Self-Host ASP.NET Web API 2

Open Web Interface for .NET (OWIN)在Web服务器和Web应用程序之间建立一个抽象层。OWIN将网页应用程序从网页服务器分离出来...

345100
来自专栏张善友的专栏

RazorEngine 3.3 在Mono 3.2上正常运行

RazorEngine 是一个简化的模板引擎基于微软新的Razor 解析引擎, Razor是在 ASP.NET MVC3 和 Web Pages中引入的。Raz...

21890
来自专栏张善友的专栏

控制ASP.NET Web API 调用频率

很多的api,例如GitHub’s API 都有流量控制的做法。使用速率限制,以防止在很短的时间量客户端向你的api发出太多的请求.例如,我们可以限制匿名API...

26690

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励