问题描述
我正在使用GridView,它可能显示50万条记录(取决于用户在过滤数据时选择的选项)。
出于优化/性能目的,我想避免这种情况。如果可以大大提高性能,我愿意将结果集限制在几千条左右。
在这种情况下,将查询以TOP(N)开头并以OPTION(FAST N)结尾是一件好事,还是会干扰GridView的内置优化(我假设,我认为读取它,当用户滚动浏览时,GridView一次只能加载这么多的记录。
所以我应该做这样的事情:
SELECT TOP (1000) [column list]
FROM [table]
WHERE [bla]
ORDER BY [Bla]
OPTION(FAST 100)
...还是我最好把它留在
SELECT [column list]
FROM [table]
WHERE [bla]
ORDER BY [Bla]
...由于我的优化尝试可能会干扰GridView已经自行处理的事情?
解决方法
这种方法很好,但可以与下面的方法一起使用 首先将对象数据源添加到页面并为此数据源设置连接并编写以下代码,并且此代码必须编写单击按钮事件
datasource1.SelectCommand = "SELECT TOP ("+txtuserfilter+") [column list] FROM [table] WHERE [bla] ORDER BY [Bla] OPTION(FAST 100)";
GridView1.DataSourceID = "datasource1";
,
GridView加载所有行。据我所知,它并没有优化任何东西。而且,如果您不为GridView(或它所在的页面)禁用ViewState,则所有这些行都将添加到ViewState,从而导致HUGE页面大小太大MB。
GridView中的分页只不过是显示第100-200行,其余行仍在加载。
所以我会做这样的事情(more info)。请注意,在这种情况下,必须使用order by
。
SELECT [column list]
FROM [table]
WHERE [bla]
ORDER BY [Bla]
OFFSET [Offset] ROWS FETCH NEXT [Pagesize] ROWS ONLY
现在,如何为GridView实现此功能。首先,创建一个存储过程。您可以不做任何事,但是因为您还需要总行数,因此需要对数据库的两个请求。在此示例中,用户ID要求数据作为WHERE
参数。
CREATE PROCEDURE [dbo].[GridView_Paging]
@offset int,@pagesize int,@userID int
AS
BEGIN
DECLARE @totalrows INT;
-- you will need the total amount of rows for paging to work
SELECT @totalrows = COUNT(itemID) FROM MyTable WHERE userid = @userID;
--select the data and add the total rows also
SELECT ID,name,@totalrows AS totalrows
FROM MyTable
WHERE userID = @userID
ORDER BY ID
OFFSET @offset ROWS FETCH NEXT @pagesize ROWS ONLY;
END
GO
然后将GridView添加到aspx页面,并确保ViewState已关闭(这节省了网络流量)。并添加一个将用作传呼机的中继器。
<asp:GridView ID="GridView1" runat="server" EnableViewState="false"></asp:GridView>
<asp:Repeater ID="Pager_GridView" runat="server">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" OnCommand="Pager_GridView_Command"
CommandArgument='<%# Container.ItemIndex %>'><%# Container.ItemIndex + 1 %>
</asp:LinkButton>
</ItemTemplate>
</asp:Repeater>
然后一个函数来调用存储过程,并将数据添加到GridView并创建寻呼机。我首先将数据添加到DataTable
而不是直接添加到GridView1.DataSource
,因为我需要总行数。
public void LoadGridView(int page,int userID)
{
int pagesize = 10;
double totalrows = 0;
int offset = page * pagesize;
//create a connection
using (var connection = new SqlConnection(Common.connectionString))
using (var command = new SqlCommand("GridView_Paging @offset,@pagesize,@userID",connection))
{
//add the parameters
command.Parameters.Add("@offset",SqlDbType.Int).Value = offset;
command.Parameters.Add("@pagesize",SqlDbType.Int).Value = pagesize;
command.Parameters.Add("@userID",SqlDbType.Int).Value = userID;
//open the connection
connection.Open();
using (var reader = command.ExecuteReader())
{
var dt = new DataTable();
dt.Load(reader);
//add the data to the gridview
GridView1.DataSource = dt;
GridView1.DataBind();
//get the total rows from the datatable
if (dt.Rows.Count > 0)
totalrows = (int)dt.Rows[0]["totalrows"];
}
}
//calculate the number of pages
var pages = Math.Ceiling(totalrows / pagesize);
//create the pager by binding an array of the correct size to the repeater
Pager_GridView.DataSource = new string[(int)pages];
Pager_GridView.DataBind();
}
然后添加Repeater LinkButton命令
protected void Pager_GridView_Command(object sender,CommandEventArgs e)
{
LoadGridView(Convert.ToInt32(e.CommandArgument),12345);
}
最后是Page_Load
。由于我已禁用ViewState,因此必须在每次页面加载和PostBack(基本上也是相同的页面加载)上绑定数据。如果您只输入LoadGridView(0,12345);
,那么您将双击数据库。进入Page_Load
,然后单击寻呼机中的链接。为防止这种情况,请检查__EVENTTARGET
是否来自中继器
protected void Page_Load(object sender,EventArgs e)
{
string eventTarget = Request.Form["__EVENTTARGET"];
//check the eventtarget contains the repeater container to prevent double hitting
if (string.IsNullOrEmpty(eventTarget) || !eventTarget.Contains(Pager_GridView.UniqueID))
{
LoadGridView(0,12345);
}
}