分页是将大量记录分成不同页面进行显示的過程。可以通过一个例子来更好地解释这一点。假设您有一个包含 10000 条记录的表,当用户请求时,您希望将它们显示给最终用户。最简单的方法就是通过一个简单的 select * from 
 查询从数据库中获取所有记录并显示它们。好的,您完成了,您的工作结束了。您已经提交了代码,您的领导或某些测试团队人员正在验证您实现的功能。假设您的经理看到了应用程序和特定功能。您能想象他的反应会是什么吗?
我可以解释一下当时的情况,因为我在编码早期就遇到过这种情况。
- 问题一:这是什么?
- 问题二:你做了什么?
- 问题三:最终用户能够阅读这么长的列表吗?
- 问题四:加载所有这些记录需要多长时间?
- 问题五:在实现此功能时,您是否考虑了此应用程序的可用性?
是的,您的经理是对的。请从最终用户的角度考虑。如果您看到这么长的列表,您该如何理解?您需要将页面一直拖到末尾才能看到最后一个用户。加载列表需要很长时间。从数据库中处理和获取如此长的列表非常耗时,有时您的应用程序可能会抛出超时异常。
在这里,我将从头开始解释到完全优化的页面。
	背景
这个简单的方法是在最后从数据库中获取我们所需的数据。考虑的要点包括减少页面渲染时间、内存管理、网络流量的益处以及完全优化的存储过程。
我们需要以下软件来开始我们的教程。
MS SQL Server 快速入门版或试用版 在此处
MS Visual Studio 2012 Ultimate 90 天试用版 在此处
好的,请按照提供的指南安装上述软件。
Using the Code
创建数据库
在 SQL Server 2012 中创建一个新数据库,如下所示,并将数据库命名为 pagination。

现在向数据库添加新表,并将其命名为 employeelist,包含以下列
EmployeeID, FirstName, LastName, Department, Location, Experience, Salary,如图所示

用于分页的 EmployeeList 表创建脚本
USE [Pagination]
GO
/****** Object:  Table [dbo].[EmployeeList]    Script Date: 10/1/2012 2:54:39 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[EmployeeList](
 [EmployeeID] [bigint] IDENTITY(1,1) NOT NULL,
 [FirstName] [varchar](200) NOT NULL,
 [LastName] [varchar](200) NOT NULL,
 [Department] [varchar](200) NOT NULL,
 [Experience] [decimal](18, 0) NOT NULL,
 [Salary] [decimal](18, 0) NOT NULL,
 CONSTRAINT [PK_EmployeeList] PRIMARY KEY CLUSTERED 
(
 [EmployeeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
现在使用以下简单的 pl-sql 程序添加 1000 行
DECLARE @intFlag INT
SET @intFlag = 1
DECLARE @Random INT
DECLARE @Upper INT
DECLARE @Lower INT
DECLARE @Exp INT
DECLARE @Sal INT
DECLARE @DEP INT
WHILE (@intFlag <=1000)
BEGIN
---- This will create a random number between 1 and 999
SET @Lower = 1 ---- The lowest random number
SET @Upper = 10 ---- The highest random number
SELECT @Exp = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SELECT @Random
---- This will create a random number between 1 and 999
SET @Lower = 10000 ---- The lowest random number
SET @Upper = 100000 ---- The highest random number
SELECT @Sal = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SELECT @Random
---- This will create a random number between 1 and 999
SET @Lower = 1 ---- The lowest random number
SET @Upper = 10 ---- The highest random number
SELECT @DEP = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SELECT @Random
INSERT INTO EmployeeList(FirstName,LastName,Department,Experience,Salary)
VALUES ('first name'+cast(@intFlag as varchar(5)),'last name'+cast(@intFlag as varchar(5)),'department'+cast(@DEP as varchar(2)),cast(@Exp as varchar(2)) ,cast(@Sal as varchar(7)))
SET @intFlag = @intFlag + 1
--IF @intFlag = 4
--BREAK;
END
GO
最后要添加的存储过程是 GetDepartments,它将从 EmployeeList 表中返回唯一的部门名称 
USE [Pagination]
GO
/****** Object:  StoredProcedure [dbo].[GetDepartments]    Script Date: 11/6/2012 1:42:55 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:<span class="Apple-tab-span" style="white-space: pre; ">		</span><Author,,Name>
-- Create date: <Create Date,,>
-- Description:<span class="Apple-tab-span" style="white-space: pre; ">	</span><Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[GetDepartments] 
<span class="Apple-tab-span" style="white-space: pre; ">	</span>-- Add the parameters for the stored procedure here
<span class="Apple-tab-span" style="white-space: pre; ">	
</span>AS
BEGIN
<span class="Apple-tab-span" style="white-space: pre; ">	</span>-- SET NOCOUNT ON added to prevent extra result sets from
<span class="Apple-tab-span" style="white-space: pre; ">	</span>-- interfering with SELECT statements.
<span class="Apple-tab-span" style="white-space: pre; ">	</span>SET NOCOUNT ON;
    -- Insert statements for procedure here
<span class="Apple-tab-span" style="white-space: pre; ">	</span>SELECT Distinct(Department) from EmployeeList order by department
END  
是的,您现在已经准备好了数据库,并且功能齐全,我们将开始处理 asp.net 部分。
创建新应用程序 
打开 visual studio 2012 并单击新建项目。它将打开现有的模板。在 Web 模板下,为我们的情况选择空网站,并将其命名为 Pagination。

现在向应用程序添加一个新 aspx 页面,如下图所示,并将其命名为 employeelist。

快速添加一个 ListView 并将其命名为 lvEmployeeList,并设计用于显示员工列表的模板,页面将如下所示
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EmployeeList.aspx.cs" Inherits="Pagination.EmployeeList" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:ListView ID="lvEmployeeList" runat="server">
                <LayoutTemplate>
                    <table style="width: 100%; text-align: left;">
                        <tr>
                            <td style="width: 20%; text-align: center;">First Name</td>
                            <td style="width: 20%; text-align: center;">Last Name</td>
                            <td style="width: 20%; text-align: center;">Department</td>
                            <td style="width: 20%; text-align: center;">Experience</td>
                            <td style="width: 20%; text-align: center;">Salary</td>
                        </tr>
                        <tr runat="server" id="itemPlaceHolder"></tr>
                    </table>
                </LayoutTemplate>
                <ItemTemplate>
                    <tr id="itemPlaceHolder">
                        <td style="width: 20%; text-align: center;"><%#Eval("FirstName") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("LastName") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("Department") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("Experience") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("Salary") %></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </div>
    </form>
</body>
</html>
我们已经完成了前端的渲染和员工详细信息的显示。现在我们需要编写一个方法来从 sql server 获取行。
using System; 
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
namespace Pagination
{
    public partial class EmployeeList : System.Web.UI.Page
    {
        string connectionstring;
        protected void Page_Load(object sender, EventArgs e)
        {
            
            connectionstring = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["PaginationConnectionString"].ConnectionString;
            if (!IsPostBack)
                GetDetails();
        }
        private void GetDetails()
        {
            DataTable dtEmployeeList = new DataTable("EmployeeList");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("select * from employeelist");
                cmd.Connection = con;
                con.Open();
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtEmployeeList);
                }
            }
            lvEmployeeList.DataSource = dtEmployeeList;
            lvEmployeeList.DataBind();
        }
    }
}
好的,一切都准备好了,我们的数据库中有 1000 条记录。我们有代码来获取并显示它们。我们快速运行并查看结果。

哇!太棒了,页面不到一秒就加载好了,真棒。好的,我们试试更多记录。在接下来的部分中,我将详细解释每个部分,并提供加载时间和性能优化方面的说明。
现在我添加了超过 200,000 条记录,当我运行同一个页面时,我的系统卡住了,原因很简单。应用程序内存不足以处理如此大量的记录集。并且从 SQL Server 处理和获取这些记录的时间非常糟糕。
那么解决方案是什么?
值得考虑的要点
优化数据库查询
优化索引
实现客户端分页
少用服务器控件
我将在这里解释每一步,以及处理记录集所需的时间。
第一步:使用 Jetbrain's Dotrace 剖析工具计算当前页面的性能(页面加载时间)。Dotrace 工具非常容易进行应用程序剖析,开销很小。我一直在使用它,已有 4 年了。它完美地显示了每一行和方法的执行时间。因此,根据 dotrace 报告,您可以相应地调整、优化或重构您的代码。
加载初始 10000 条记录所需的时间为 765 毫秒。请查看下图

现在我们将尝试使用 100000 条记录来检查性能。性能记录如下。

请注意区别,随着记录数量的增长,加载时间也随之增长。这里我只显示 Page load 方法的执行时间。所以这里看到的时间非常少,不到完整页面渲染和方法执行时间的一半。假设加载和渲染 100000 条记录需要 3 分钟,但这里显示的 dotrace 报告只是方法的执行时间。
第一步:优化数据库查询
我们可以通过多种方式优化数据库查询。首先要检查的是我们需要哪些列?
假设我们只需要姓名、部门和经验。更改查询并查看结果,如下所示
SELECT [FirstName]+' '+[LastName] Name,[Department],[Experience] FROM EmployeeList

您可以看到这里的变化,渲染结果到页面减少了 3000 毫秒。因此,要渲染到页面的内容以及需要在网络上传递的结果集都减少了,这就是变化的原因。
同样,如果我们能减少行数,即通过应用过滤器来减少每页的行数,这将为最终用户提供最佳的可用性,并为应用程序提供最佳的性能。因此,让我们选择三个列来过滤记录集:姓名、部门和经验。
相应地更改存储过程代码以使用这些参数。
USE [Pagination]
GO
/****** Object:  StoredProcedure [dbo].[GetEmployeeList]    Script Date: 10/13/2012 4:09:34 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GetEmployeeList] 
 @name varchar(200)=null,
 @departmanet varchar(200)=null,
 @expstart int = null,
 @expend int=null
AS
BEGIN
 SET NOCOUNT ON;
 SELECT top 100000 [FirstName]+' '+[LastName] Name,[Department],[Experience] FROM EmployeeList
 where 
 (FirstName like '%'+@name+'%' or @name is null)
 and (LastName like '%'+@name+'%' or @name is null)
 and (Department =@departmanet or @departmanet is null)
 and ((Experience >@expstart or @expstart is null) and (Experience<@expend or @expend is null ) )
END
这将为您提供精确的查询结果,并获得最佳的应用程序性能。修改您的屏幕,以便最终用户可以输入参数值,如下图所示

并像下面一样修改 Codebehind
 private void GetDetails() 
        {
            DataTable dtEmployeeList = new DataTable("EmployeeList");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("GetEmployeeList");
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                con.Open();
                cmd.Parameters.Add(new SqlParameter("@name", txtName.Text.Trim()));
                if (ddlDepartment.SelectedIndex > 0)
                    cmd.Parameters.Add(new SqlParameter("@departmanet", ddlDepartment.SelectedValue));
                if (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0)
                {
                    cmd.Parameters.Add(new SqlParameter("@expstart", ddlStart.SelectedValue));
                    cmd.Parameters.Add(new SqlParameter("@expend", ddlStart0.SelectedValue));
                }
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtEmployeeList);
                }
            }
            lvEmployeeList.DataSource = dtEmployeeList;
            lvEmployeeList.DataBind();
        }
        
        
好的,现在运行您的应用程序,并提供如图所示的详细信息,并运行您的 dotrace 工具来测量应用程序性能。

性能概览

哇!我们获得了惊人的性能,仅用 416 毫秒就从 sql server 获取了记录,尽管它们是从 10 万条记录中过滤出来的。好的,这种方法仍然存在问题,因为今天只有 13000 多条记录,如果将来同一标准下添加更多记录怎么办?我们需要进一步优化以获得相同的性能。好的,我们将在下一篇文章中看到。
客户端分页
我们已经完成了数据过滤,这样可以减少数据库中的记录集,从而减少网络延迟并提高数据传输和渲染时间。尽管如此,我们仍然返回超过 10000 条记录,这对于最终用户来说无法一目了然。在这里,我们的分页起到了最好的作用,一次显示应用程序中的一组记录,并通过在页面之间导航来不断移至下一组。
优点
客户端分页
- 这将提高性能
- 用户将有时间单独审查每条记录,而无需滚动页面查找所有记录
- 减少数据到页面的渲染时间
- 提供最佳的应用程序性能
我们现在将检查优势列表。
在这里,我们需要做一个简单的事情来为 listview、gridview、datalist 或任何数据控件启用分页,方法是使用 DataPager 对象,它将为我们当前的实践提供最佳效果。现在,将 DataPager 添加到您的代码中。
修改您的 aspx 代码如下
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EmployeeList.aspx.cs" Inherits="Pagination.EmployeeList" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type="text/css">
        .auto-style1
        {
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <table class="auto-style1">
                <tr>
                    <td colspan="2" style="text-align: left;"><strong>Employee List</strong></td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Name:</td>
                    <td>
                        <asp:TextBox ID="txtName" runat="server" Width="40%"></asp:TextBox>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Department:</td>
                    <td>
                        <asp:DropDownList ID="ddlDepartment" runat="server" Width="41%">
                        </asp:DropDownList>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Experience Start</td>
                    <td>
                        <asp:DropDownList ID="ddlStart" runat="server" Width="20%">
                            <asp:ListItem Value="0">--Start--</asp:ListItem>
                            <asp:ListItem>1</asp:ListItem>
                            <asp:ListItem>2</asp:ListItem>
                            <asp:ListItem>3</asp:ListItem>
                            <asp:ListItem>4</asp:ListItem>
                            <asp:ListItem>5</asp:ListItem>
                            <asp:ListItem>6</asp:ListItem>
                            <asp:ListItem>7</asp:ListItem>
                            <asp:ListItem>8</asp:ListItem>
                            <asp:ListItem>9</asp:ListItem>
                            <asp:ListItem>10</asp:ListItem>
                        </asp:DropDownList>
                        <asp:DropDownList ID="ddlStart0" runat="server" Width="20%">
                            <asp:ListItem Value="0">--End--</asp:ListItem>
                            <asp:ListItem>1</asp:ListItem>
                            <asp:ListItem>2</asp:ListItem>
                            <asp:ListItem>3</asp:ListItem>
                            <asp:ListItem>4</asp:ListItem>
                            <asp:ListItem>5</asp:ListItem>
                            <asp:ListItem>6</asp:ListItem>
                            <asp:ListItem>7</asp:ListItem>
                            <asp:ListItem>8</asp:ListItem>
                            <asp:ListItem>9</asp:ListItem>
                            <asp:ListItem>10</asp:ListItem>
                        </asp:DropDownList>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;"> </td>
                    <td>
                        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Search>>" />
                    </td>
                </tr>
                <tr>
                    <td colspan="2" style="text-align: left;">
                        <asp:Label ID="lblQuery" runat="server"></asp:Label>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;"> </td>
                    <td> </td>
                </tr>
            </table>
            <br />
            <asp:ListView ID="lvEmployeeList" runat="server">
                <LayoutTemplate>
                    <table style="width: 100%; text-align: left;">
                        <tr>
                            <%--<td style="width: 20%; text-align: center; font-weight: bolder;">First Name</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Last Name</td>--%>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Name</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Department</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Experience</td>
                            <%--<td style="width: 20%; text-align: center; font-weight: bolder;">Salary</td>--%>
                        </tr>
                        <tr runat="server" id="itemPlaceHolder"></tr>
                    </table>
                </LayoutTemplate>
                <ItemTemplate>
                    <tr id="itemPlaceHolder">
                        <td style="width: 20%; text-align: center;"><%#Eval("Name") %></td>
                        <%--<td style="width: 20%; text-align: center;"><%#Eval("LastName") %></td>--%>
                        <td style="width: 20%; text-align: center;"><%#Eval("Department") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("Experience") %></td>
                        <%--<td style="width: 20%; text-align: center;"><%#Eval("Salary") %></td>--%>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
            <asp:DataPager ID="dpEmployees" PageSize="100" PagedControlID="lvEmployeeList" OnPreRender="dpEmployees_PreRender" runat="server">
                <Fields>
                    <asp:NextPreviousPagerField  ButtonType="Link"/>
                </Fields>
            </asp:DataPager>
        </div>
    </form>
</body>
</html>
修改您的 codebhind 如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
namespace Pagination
{
    public partial class EmployeeList : System.Web.UI.Page
    {
        string connectionstring;
        protected void Page_Load(object sender, EventArgs e)
        {
            lblQuery.Text = string.Empty;
            connectionstring = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["PaginationConnectionString"].ConnectionString;
            if (!IsPostBack)
            {
                LoadDepartment();
            }
        }
        private void LoadDepartment()
        {
            DataTable dtDeps = new DataTable("Deps");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("GetDepartments");
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                con.Open();
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtDeps);
                }
            }
            ddlDepartment.DataSource = dtDeps;
            ddlDepartment.DataTextField = "Department";
            ddlDepartment.DataValueField = "Department";
            ddlDepartment.DataBind();
            ddlDepartment.Items.Insert(0, new ListItem("--Select--", ""));
        }
        private void GetDetails()
        {
            DataTable dtEmployeeList = new DataTable("EmployeeList");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("GetEmployeeList");
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                con.Open();
                cmd.Parameters.Add(new SqlParameter("@name", txtName.Text.Trim()));
                if (ddlDepartment.SelectedIndex > 0)
                    cmd.Parameters.Add(new SqlParameter("@departmanet", ddlDepartment.SelectedValue));
                if (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0)
                {
                    cmd.Parameters.Add(new SqlParameter("@expstart", ddlStart.SelectedValue));
                    cmd.Parameters.Add(new SqlParameter("@expend", ddlStart0.SelectedValue));
                }
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtEmployeeList);
                }
            }
            lblQuery.Text += "<br/><br/>Number Of Records " + dtEmployeeList.Rows.Count;
            lvEmployeeList.DataSource = dtEmployeeList;
            lvEmployeeList.DataBind();
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
        }
        protected void dpEmployees_PreRender(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtName.Text.Trim()) || ddlDepartment.SelectedIndex > 0 || (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0))
            { 
                string str = "Select criteria"; 
                str += " Name is like '%" + txtName.Text.Trim() + "%'";
                if (ddlDepartment.SelectedIndex > 0)
                    str += " Department='" + ddlDepartment.SelectedValue + "'";
                if (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0)
                    str += " Experience range ' FROM " + ddlStart.SelectedValue + " TO " + ddlStart0.SelectedIndex + "'";
                lblQuery.Text = str;
                GetDetails();
            }
        }
    }
}
现在运行您的代码并查看结果

哇!太棒了,您可以检查结果并查看我们客户端分页的惊人性能。这只是我们 GetDetails() 方法仅用 259 毫秒从数据库获取记录,这正好是过滤记录所需时间的一半。数据绑定时间也减少到 75 毫秒,因此我们的页面在 1 秒内渲染完成,这对于页面加载来说至关重要。
并且这是已经删除了服务器控件的版本,我希望不需要再次解释。
如果您使用任何状态管理对象将这些记录集保存在客户端,性能会进一步提高。我将在接下来的文章中解释状态管理。
到目前为止我们讨论的内容足以处理正常机器上超过 100 万条记录,这将为您提供有史以来最佳的应用程序性能。在下一篇文章中,我将解释服务器端分页。
服务器端分页
现在我们将讨论服务器端分页,当数据库包含数百万、数十亿甚至数万亿条记录时,它能够非常高效地在不到一秒的时间内显示记录。
服务器端分页如何工作。
我们为服务器端分页做什么?
下面是更改后的过程,请看一下更改
我一直在我的笔记本电脑上解释一切,它的配置是 4GB RAM、500GB 硬盘和 INTEL I7 处理器。这就是为什么我们获得了非常好的性能。如果数据库位于我的本地网络、办公室网络或互联网上的某个位置,会怎么样?我们返回的数据量、网络速度和网络延迟都会受到影响。
这里的服务器端分页概念是限制每次请求的记录数量。而不是将 100000 条记录发送到客户端并让客户端实现我们在上一篇文章中所做的所有步骤或实践,这是浪费。所以我们只需要多少记录,就只传输多少到客户端。通过这种方式,我们可以克服客户端网络难题。
使用 CTE(通用表表达式)对普通存储过程进行少量修改
通用表表达式 (CTE) 可以被看作是一个临时结果集,它在一个 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 语句的执行范围内定义。
USE [Pagination]
GO
/****** Object:  StoredProcedure [dbo].[GetEmployeeList]    Script Date: 10/17/2012 11:29:34 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetEmployeeList] 
@pageIndex int,
@pageSize int,
 @name varchar(200)=null,
 @departmanet varchar(200)=null,
 @expstart int = null,
 @expend int=null
AS
BEGIN
 SET NOCOUNT ON;
 DECLARE @start int, @end int;
 Declare @result table(RowNum int, Name varchar(500),Department varchar(200),Experience int)
SELECT @start = (@pageIndex - 1) * @pageSize + 1,
  @end = @start + @pageSize - 1;
 WITH ABC AS
 (
 SELECT ROW_NUMBER() OVER (ORDER BY EmployeeID) RowNum,[FirstName]+' '+[LastName] Name,[Department],[Experience] 
  FROM EmployeeList
 where 
 (FirstName like '%'+@name+'%' or @name is null)
 and (LastName like '%'+@name+'%' or @name is null)
 and (Department =@departmanet or @departmanet is null) 
 and ((Experience >@expstart or @expstart is null) and (Experience<@expend or @expend is null ) )
 ) 
 insert into @result select count(*),null,null,null from abc union all 
 SELECT * FROM ABC WHERE RowNum BETWEEN @start and @end 
 select * from @result 
END
您需要相应地更改客户端代码以适应此功能。查看下面的最终屏幕

您的 ASPX 代码将是
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EmployeeList.aspx.cs" Inherits="Pagination.EmployeeList" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
            <table class="auto-style1">
            <asp:ListView ID="lvEmployeeList" runat="server">
                </ItemTemplate>
        </div>
<head runat="server">
    <title></title>
    <style type="text/css">
        .auto-style1
        {
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <div>
                <tr>
                    <td colspan="2" style="text-align: left;"><strong>Employee List</strong></td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Name:</td>
                    <td>
                        <asp:TextBox ID="txtName" runat="server" Width="40%"></asp:TextBox>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Department:</td>
                    <td>
                        <asp:DropDownList ID="ddlDepartment" runat="server" Width="41%">
                        </asp:DropDownList>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;">Experience Start</td>
                    <td>
                        <asp:DropDownList ID="ddlStart" runat="server" Width="20%">
                            <asp:ListItem Value="0">--Start--</asp:ListItem>
                            <asp:ListItem>1</asp:ListItem>
                            <asp:ListItem>2</asp:ListItem>
                            <asp:ListItem>3</asp:ListItem>
                            <asp:ListItem>4</asp:ListItem>
                            <asp:ListItem>5</asp:ListItem>
                            <asp:ListItem>6</asp:ListItem>
                            <asp:ListItem>7</asp:ListItem>
                            <asp:ListItem>8</asp:ListItem>
                            <asp:ListItem>9</asp:ListItem>
                            <asp:ListItem>10</asp:ListItem>
                        </asp:DropDownList>
                        <asp:DropDownList ID="ddlStart0" runat="server" Width="20%">
                            <asp:ListItem Value="0">--End--</asp:ListItem>
                            <asp:ListItem>1</asp:ListItem>
                            <asp:ListItem>2</asp:ListItem>
                            <asp:ListItem>3</asp:ListItem>
                            <asp:ListItem>4</asp:ListItem>
                            <asp:ListItem>5</asp:ListItem>
                            <asp:ListItem>6</asp:ListItem>
                            <asp:ListItem>7</asp:ListItem>
                            <asp:ListItem>8</asp:ListItem>
                            <asp:ListItem>9</asp:ListItem>
                            <asp:ListItem>10</asp:ListItem>
                        </asp:DropDownList>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;"> </td>
                    <td>
                        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Search>>" />
                    </td>
                </tr>
                <tr>
                    <td colspan="2" style="text-align: left;">
                        <asp:Label ID="lblQuery" runat="server"></asp:Label>
                    </td>
                </tr>
                <tr>
                    <td style="width: 25%; text-align: left;"> </td>
                    <td style="text-align: right">
                        Page Size :
                        <asp:DropDownList ID="ddlPageSize" runat="server" AutoPostBack="True" OnSelectedIndexChanged="ddlPageSize_SelectedIndexChanged">
                            <asp:ListItem>50</asp:ListItem>
                            <asp:ListItem>100</asp:ListItem>
                            <asp:ListItem>200</asp:ListItem>
                        </asp:DropDownList>
                         Page Index :
                        <asp:DropDownList ID="ddlPageIndex" runat="server" AutoPostBack="True" OnSelectedIndexChanged="ddlPageIndex_SelectedIndexChanged">
                            <asp:ListItem>1</asp:ListItem>
                        </asp:DropDownList>
                    </td>
                </tr>
            </table>
            <br />
                <LayoutTemplate>
                    <table style="width: 100%; text-align: left;">
                        <tr>
                            <%--<td style="width: 20%; text-align: center; font-weight: bolder;">First Name</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Last Name</td>--%>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Name</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Department</td>
                            <td style="width: 20%; text-align: center; font-weight: bolder;">Experience</td>
                            <%--<td style="width: 20%; text-align: center; font-weight: bolder;">Salary</td>--%>
                        </tr>
                        <tr runat="server" id="itemPlaceHolder"></tr>
                    </table>
                </LayoutTemplate>
                <ItemTemplate>
                    <tr id="itemPlaceHolder">
                        <td style="width: 20%; text-align: center;"><%#Eval("Name") %></td>
                        <%--<td style="width: 20%; text-align: center;"><%#Eval("LastName") %></td>--%>
                        <td style="width: 20%; text-align: center;"><%#Eval("Department") %></td>
                        <td style="width: 20%; text-align: center;"><%#Eval("Experience") %></td>
                        <%--<td style="width: 20%; text-align: center;"><%#Eval("Salary") %></td>--%>
                    </tr>
            </asp:ListView>
    </form>
</body>
</html>
后台代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
namespace Pagination
{
    public partial class EmployeeList : System.Web.UI.Page
    {
        string connectionstring;
        protected void Page_Load(object sender, EventArgs e)
        {
            lblQuery.Text = string.Empty;
            connectionstring = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["PaginationConnectionString"].ConnectionString;
            if (!IsPostBack)
            {
                LoadDepartment();
            }
        }
        private void LoadDepartment()
        {
            DataTable dtDeps = new DataTable("Deps");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("GetDepartments");
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                con.Open();
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtDeps);
                }
            }
            ddlDepartment.DataSource = dtDeps;
            ddlDepartment.DataTextField = "Department";
            ddlDepartment.DataValueField = "Department";
            ddlDepartment.DataBind();
            ddlDepartment.Items.Insert(0, new ListItem("--Select--", ""));
        }
        private void GetDetails()
        {
            DataTable dtEmployeeList = new DataTable("EmployeeList");
            using (SqlConnection con = new SqlConnection(connectionstring))
            {
                SqlCommand cmd = new SqlCommand("GetEmployeeList");
                cmd.Connection = con;
                cmd.CommandType = CommandType.StoredProcedure;
                con.Open();
                cmd.Parameters.Add(new SqlParameter("@name", txtName.Text.Trim()));
                if (ddlDepartment.SelectedIndex > 0)
                    cmd.Parameters.Add(new SqlParameter("@departmanet", ddlDepartment.SelectedValue));
                cmd.Parameters.Add(new SqlParameter("@pageIndex", ddlPageIndex.SelectedValue));
                cmd.Parameters.Add(new SqlParameter("@pageSize", ddlPageSize.SelectedValue));
                if (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0)
                {
                    cmd.Parameters.Add(new SqlParameter("@expstart", ddlStart.SelectedValue));
                    cmd.Parameters.Add(new SqlParameter("@expend", ddlStart0.SelectedValue));
                }
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    sda.Fill(dtEmployeeList);
                }
            }
            int records = int.Parse(dtEmployeeList.Rows[0][0].ToString());
            lblQuery.Text += "<br/><br/>Number Of Records " + records;
            int possibleindexes = records / int.Parse(ddlPageSize.SelectedValue);
            ddlPageIndex.Items.Clear();
            for (int i = 1; i <= possibleindexes; i++)
                ddlPageIndex.Items.Add(i.ToString());
            dtEmployeeList.Rows.Remove(dtEmployeeList.Rows[0]);
            lvEmployeeList.DataSource = dtEmployeeList;
            lvEmployeeList.DataBind();
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            string str = "Select criteria";
            str += " Name is like '%" + txtName.Text.Trim() + "%'";
            if (ddlDepartment.SelectedIndex > 0)
                str += " Department='" + ddlDepartment.SelectedValue + "'";
            if (ddlStart.SelectedIndex > 0 && ddlStart0.SelectedIndex > 0)
                str += " Experience range ' FROM " + ddlStart.SelectedValue + " TO " + ddlStart0.SelectedIndex + "'";
            lblQuery.Text = str;
            GetDetails();
        }
        protected void ddlPageIndex_SelectedIndexChanged(object sender, EventArgs e)
        {
            GetDetails();
        }
        protected void ddlPageSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            GetDetails();
        }
    }
}
现在我们将看到结果

太棒了!!!您可以检查应用程序性能,现在我们看到了最佳的应用程序性能。执行 GetDetails() 方法仅需 177 毫秒,性能达到了最佳。我们到目前为止付出了多少努力。分页在这些场景下效果非常好。
客户端分页与服务器端分页
现在是时候比较这两种场景和实现方式了。
客户端分页 
	- 考虑在不可能进行服务器端分页时
- 需要状态管理来减少数据库调用。即在缓存、会话、ViewState 和静态对象中存储结果集。
- 内存消耗非常大
- 网络速度和延迟会受到影响
- 另一方面,客户端分页的优点是可以一次性获取所有结果,然后在这些数据上进行操作,而无需反复连接数据库。
- 应用程序中的所有内容都将可用,因此对于与新记录进行比较和匹配将非常有用。
- 仅当应用程序需要记录集时才考虑使用
服务器端分页
	- 我们可以克服网络问题
- 与所有方法相比速度非常快
- 所需精力最少
- 在处理高流量应用程序时非常重要
- 无法为应用程序实现缓存或本地存储
- 当您的应用程序性能非常差时考虑使用
关注点
这是 MS .NET 开发人员的常规实践,但对于初学者和高级开发人员来说都非常有益,因为性能始终很重要,并且在面试中经常会遇到有关加载记录的问题。
  ©  . All rights reserved.