使用 TOP (N) 和 OPTION(FAST N) 会干扰 GridView 的内置优化吗?
Would using TOP (N) and OPTION(FAST N) interfere with GridView's built-in Optimizations?
我正在使用可能显示 50 万条记录的 GridView(取决于用户在过滤数据时选择的选项)。
出于 optimization/performance 目的,我想阻止这种情况。如果它能显着提高性能,我愿意将结果集限制在几千条左右。
对于这种情况,以 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,导致页面大小超过 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 关闭(这样可以节省网络流量)。并添加一个 Repeater 作为寻呼机。
<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);
那么你就是 double-hitting 数据库。在 Page_Load
中一次,在寻呼机中单击 link 一次。为防止这种情况,请检查 __EVENTTARGET
是否来自 Repeater
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);
}
}
我正在使用可能显示 50 万条记录的 GridView(取决于用户在过滤数据时选择的选项)。
出于 optimization/performance 目的,我想阻止这种情况。如果它能显着提高性能,我愿意将结果集限制在几千条左右。
对于这种情况,以 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,导致页面大小超过 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 关闭(这样可以节省网络流量)。并添加一个 Repeater 作为寻呼机。
<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);
那么你就是 double-hitting 数据库。在 Page_Load
中一次,在寻呼机中单击 link 一次。为防止这种情况,请检查 __EVENTTARGET
是否来自 Repeater
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);
}
}