65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Repeater 和 SQL Server 2005 / 2008 实现高效分页

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (6投票s)

2011年12月16日

CPOL

2分钟阅读

viewsIcon

43334

downloadIcon

708

使用 Repeater 和 SQL Server 2005 中的 OVER() 关键字实现高效分页。

引言

这里提供的代码使用 ASP.NET Repeater 和 SQL Server 2005/2008(而非 2000)提供优化的分页解决方案。该实现使用临时表、ROW_COUNT 或类似方法。它仅返回指定的行数,而不是完整数据集,以提高性能。该代码旨在非常简单,易于维护。

背景

分页在许多地方都有应用,但很难找到一种快速、高效且易于实现的优化解决方案。通过这篇文章,我尝试实现我认为高效的一些技术。欢迎所有评论和代码优化建议。

使用代码

要使用此代码,您必须拥有 SQL Server 2005、2008 或更高版本。Express 版本也可以工作。

  1. 首先运行 paging.sql 文件 - 它将创建一个表 [Employees],一个视图 [Paging_v] 和一个存储过程 [Paging]。 代码如下
  2. -- Create the table
    CREATE TABLE dbo.Employees 
    (
     EmployeeID int identity PRIMARY KEY,
     FirstName nvarchar(64),
     LastName nvarchar(64)
    )
    GO
    
    -- Create the view
    CREATE VIEW [dbo].[Paging_v]
    AS
    SELECT     EmployeeID, FirstName, LastName
    FROM         dbo.Employees
    GO
    
    
    -- Create the stored procedure
    CREATE PROCEDURE [dbo].[Paging]
    (
      @Page int,
      @PageSize int
    )
    AS
    SET NOCOUNT ON;
    
    SELECT TOP(@PageSize) * FROM 
    (
     SELECT RowID = ROW_NUMBER() OVER (ORDER BY EmployeeID), 
      EmployeeID, FirstName, LastName, TotalRows=Count(*) OVER()
     FROM Paging_v  
    ) 
    A WHERE A.RowID > ((@Page-1)*@PageSize)
    SET NOCOUNT OFF

    如您在存储过程中所见,我将 ROW_NUMBER() OVER 语句与 SQL TOP(@PageSize) 结合使用,以仅返回请求的行数。 与 #temp 表相比,这非常高效。

  3. 现在将一些自定义数据加载到 [Employee] 表中 - 例如,添加 10-15 行用于测试。 我已经使用超过 1,000,000 行的数据测试了该代码,并且仍然高效。
  4. 以下代码是 paging.aspx 文件
  5. <%@ Page Language="C#" AutoEventWireup="true" 
             CodeFile="paging.aspx.cs" 
             Inherits="paging" EnableViewState="true" %>
    <html>
    <head>
    <style type="text/css">
    th {text-align:left;}
    div.paging a {padding:0 3 0 3;}
    </style>
    </head>
    
    <body>
    <form runat="server">
    <asp:ScriptManager runat="server" />
    
    <asp:UpdatePanel ID="up1" runat="server">
    <ContentTemplate>
    
    <asp:Repeater ID="rep1" runat="server">
    <HeaderTemplate>
    <table style="width:100%">
    <thead>
        <tr><th>ID</th><th>First</th><th>Last</th></tr>
    </thead>
    <tbody>
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
        <td><%# Eval("EmployeeID")%></td>
        <td><%# Eval("FirstName") %></td>
        <td><%# Eval("LastName") %></td>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
    </tbody>
    </table>
    </FooterTemplate>
    </asp:Repeater>
    <br />
    
    <div class="paging"><asp:PlaceHolder ID="plcPaging" 
       runat="server" /> <asp:HiddenField ID="hdCount" 
       runat="server" Value="0" /></div>
    
    </ContentTemplate>
    </asp:UpdatePanel>
    
    </form>
    </body>
    </html>

    我将 Repeater 封装在 UpdatePanel 中,但您可以根据需要删除它。有一个用于实际分页器的占位符,名为 plcPaging,例如“1 2 3 4 5 6 ...”,以及一个隐藏字段,其中包含总行数。

  6. 以下是 paging.aspx.cs 文件 - 我正在使用 Microsoft Enterprise Library 从 SQL Server 获取数据,但您可以使用任何您喜欢的提供程序。
  7. using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Text;
    using Microsoft.Practices.EnterpriseLibrary.Data;
    using System.Data.Common;
    
    public partial class paging : System.Web.UI.Page
    {
        int pageSize = 15;
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                getData(1); // initially get data from the first page
                createPaging();
            }
            else
            {
                plcPaging.Controls.Clear();
                createPaging();
            }
        }
    
        private void getData(int Page)
        {
            DataTable dt = PagingData(Page, pageSize);
            if (dt.Rows.Count > 0)
            {
                hdCount.Value = dt.Rows[0]["TotalRows"].ToString();
                rep1.DataSource = dt;
                rep1.DataBind();
            }
        }
    
        private DataTable PagingData(int Page, int PageSize)
        {
            // talk to database and return datatable
            Database db = DatabaseFactory.CreateDatabase();
            using (DbCommand cmd = db.GetStoredProcCommand("Paging"))
            {
                db.AddParameter(cmd, "@Page", DbType.Int32, 4, 
                  ParameterDirection.Input, true, 10, 0, null, DataRowVersion.Default, Page);
                db.AddParameter(cmd, "@PageSize", DbType.Int32, 4, 
                  ParameterDirection.Input, true, 10, 0, null, DataRowVersion.Default, PageSize);
                using (DataTable dt = (db.ExecuteDataSet(cmd)).Tables[0])
                {
                    return dt;
                }
            }
        }
    
        protected void pager_Click(object sender, EventArgs e)
        {
            LinkButton lnk = sender as LinkButton;
            int currentPage = int.Parse(lnk.Text);
            getData(currentPage);
        }
    
        private void createPaging()
        {
            int rowCount = Convert.ToInt32(hdCount.Value.ToString());
            if (rowCount <= pageSize)
            // don't create the pager if there are less rows than specified pageSize.
                return;
    
            // e.g. 9 % 5 = 4 - means we have an extra page,
            // so add 1 to rowCount otherwise add 0
            rowCount = rowCount / pageSize + (rowCount % pageSize != 0 ? 1 : 0); 
    
            for (int i = 0; i < rowCount; i++) 
            {
                LinkButton lnk = new LinkButton();
                lnk.Click += new EventHandler(pager_Click);
                lnk.ID = "p" + (i).ToString();
                lnk.Text = (i+1).ToString();
                plcPaging.Controls.Add(lnk);
            }
        }    
    }

解释

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        getData(1); // initially get data from the first page
        createPaging();
    }
    else
    {
        plcPaging.Controls.Clear();
        createPaging();
    }
}

Page_Load 中,我们只是检查是否存在回发。 如果没有发生回发,我们使用 getData(1) 加载第一页数据。 调用 createPaging() 方法为数据集中的每个页面创建链接。

接下来,在 getData(int Page) 中,我们通过 PagingData(Page,pageSize) 方法从数据库获取数据。 如前所述,您可以使用任何提供程序,但我使用了 Enterprise Library 以提高清晰度。

private void getData(int Page)
{
    DataTable dt = PagingData(Page, pageSize);
    if (dt.Rows.Count > 0)
    {
        hdCount.Value = dt.Rows[0]["TotalRows"].ToString();
        rep1.DataSource = dt;
        rep1.DataBind();
    }
}

private DataTable PagingData(int Page, int PageSize)
{
    // talk to database and return datatable
    Database db = DatabaseFactory.CreateDatabase();
    using (DbCommand cmd = db.GetStoredProcCommand("Paging"))
    {
        db.AddParameter(cmd, "@Page", DbType.Int32, 4, 
          ParameterDirection.Input, true, 10, 0, null, DataRowVersion.Default, Page);
        db.AddParameter(cmd, "@PageSize", DbType.Int32, 4, 
          ParameterDirection.Input, true, 10, 0, null, DataRowVersion.Default, PageSize);
        using (DataTable dt = (db.ExecuteDataSet(cmd)).Tables[0])
        {
            return dt;
        }
    }
}

最后,我正在使用 EventHandler 来处理 plcPaging 控件中链接的单击事件。

protected void pager_Click(object sender, EventArgs e)
{
    LinkButton lnk = sender as LinkButton;
    int currentPage = int.Parse(lnk.Text);
    getData(currentPage);
}

private void createPaging()
{
    int rowCount = Convert.ToInt32(hdCount.Value.ToString());
    if (rowCount <= pageSize)
    // don't create the pager if there are less rows than specified pageSize.
        return;

    // e.g. 9 % 5 = 4 - means we have an extra page, so add 1 to rowCount otherwise add 0
    rowCount = rowCount / pageSize + (rowCount % pageSize != 0 ? 1 : 0); 

    for (int i = 0; i < rowCount; i++) 
    {
        LinkButton lnk = new LinkButton();
        lnk.Click += new EventHandler(pager_Click);
        lnk.ID = "p" + (i).ToString();
        lnk.Text = (i+1).ToString();
        plcPaging.Controls.Add(lnk);
    }
}

分页仅生成数字,如“1 2 3 4 5 6” - 它不会生成“上一页”和“下一页”链接 - 欢迎提出任何反馈。

历史

  • 2011/12/16 - 实现了版本 1。
© . All rights reserved.