asp.net安全、实用、简单的大容量存储过程分页第1/2页

问题描述:

昨晚研究到2点多,对网络上主流的分页存储过程大体看了一遍,但对安全以及如何使用很多文章都没有过多的提及,而我要在这些文章的基础上总结出一个比较实用的分页存储过程,方便大家在以后的项目中使用。


开始测试:
在BLL层(业务逻辑层),DAL层(数据访问层),DB(访问层基类),WEB(网站),关于3层结构这里就不介绍了,大家可以看看我前面写过的文章《.net三层结构初探》 。
先添加存储过程:

复制代码 代码如下:

--阿会楠根据网络上的代码进行了修改,版权归原作者所有2009-4-5
--修改输出总记录数


create PROCEDURE [dbo].[pagination]
@tblName varchar(255), -- 表名
@strGetFields varchar(1000), -- 需要返回的列
@fldName varchar(255), -- 排序的字段名
@PageSize int, -- 页尺寸
@PageIndex int, -- 页码
@OrderType bit, -- 设置排序类型, 非 0 值则降序
@strWhere varchar(1500), -- 查询条件 (注意: 不要加 where)
@rowCount int output --查询到的记录数
AS

declare @strSQL varchar(5000) -- 主语句
declare @strTmp varchar(110) -- 临时变量
declare @strOrder varchar(400) -- 排序类型
declare @strCountTmp nvarchar(100) --记录数
declare @timediff datetime --耗时

begin
select @timediff = Getdate()
if @strWhere !=''
set @strCountTmp = 'select @rowCount = count(*) from [' + @tblName + '] where '+@strWhere
else
set @strCountTmp = 'select @rowCount = rowCount_tmp from tmp where (table_tmp = ''' + @tblName +''')'

exec sp_executesql @strCountTmp,N'@rowCount int out',@rowCount out --输出总记录数

if @OrderType != 0
begin
set @strTmp = '<(select min'
set @strOrder = ' order by ' + @fldName +' desc'
--如果@OrderType不是0,就执行降序,这句很重要!
end

else
begin
set @strTmp = '>(select max'
set @strOrder = ' order by ' + @fldName +' asc'
end
if @PageIndex = 1
begin
if @strWhere != ''
set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ ' from [' + @tblName + '] where ' + @strWhere + ' ' + @strOrder
else
set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ ' from ['+ @tblName + '] '+ @strOrder
--如果是第一页就执行以上代码,这样会加快执行速度
end

else
begin
--以下代码赋予了@strSQL以真正执行的SQL代码
set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ ' from ['
+ @tblName + '] where [' + @fldName + ']' + @strTmp + '(['+ @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['+ @fldName + '] from [' + @tblName + ']' + @strOrder + ') as tblTmp)'+ @strOrder
if @strWhere != ''
set @strSQL = 'select top ' + str(@PageSize) +' '+@strGetFields+ ' from ['
+ @tblName + '] where [' + @fldName + ']' + @strTmp + '(['
+ @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['
+ @fldName + '] from [' + @tblName + '] where ' + @strWhere + ' '
+ @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder
end

end
exec (@strSQL)
select datediff(ms,@timediff,GetDate()) as runtime

这个SQL存储过程语句非常的好,但是不足的地方就是如果你的条件语句中含有'(单引号)那就很郁闷了,十分的麻烦。你可以试下改动上面的相关代码如下:
复制代码 代码如下:

replace(@strWhere,'''','''''')

不放心自己测试下吧。
复制代码 代码如下:

USE [data_Test]
GO

DECLARE @return_value int,
@rowCount int

EXEC @return_value = [dbo].[pagination]
@tblName = 'userinfo',
@strGetFields = N'id',
@fldName = N'id',
@PageSize = 10,
@PageIndex = 300,
@OrderType = 0,
@strWhere = null,
@rowCount = @rowCount OUTPUT

SELECT @rowCount as N'@rowCount'

SELECT 'Return Value' = @return_value

GO

最主要的DAL层代码:
复制代码 代码如下:

public DataSet GetList(int PageIndex, string strWhere,ref int rowCount)
{
SqlParameter[] parameter = sosuo8.DBUtility.DbHelperSQL.pagePara();

parameter[0].Value = "userinfo";
parameter[1].Value = "id,userName,userWebName,createDate";
parameter[2].Value = "id";
parameter[3].Value = 10;
parameter[4].Value = PageIndex;
parameter[5].Value = 0;
parameter[6].Value = strWhere;
parameter[7].Direction = ParameterDirection.Output;//声明为输出类型

DataSet ds = sosuo8.DBUtility.DbHelperSQL.RunProcedure("pagination",parameter,"userinfo");
rowCount = Convert.ToInt32(parameter[7].Value);
return ds;
}

全部代码:
复制代码 代码如下:

public DataSet GetList(int iPageSize, int iPageIndex, string strWhere, ref int iCount)
{

SqlParameter[] parameters ={
new SqlParameter("@tblName",SqlDbType.VarChar,255),
new SqlParameter("@strGetFields",SqlDbType.VarChar,1000),
new SqlParameter("@fldName",SqlDbType.VarChar,255),
new SqlParameter("@PageSize",SqlDbType.Int),
new SqlParameter("@PageIndex",SqlDbType.Int),
new SqlParameter("@OrderType",SqlDbType.Int),
new SqlParameter("@strWhere",SqlDbType.NVarChar,2000),
new SqlParameter("@rowCount",SqlDbType.Int),
};
parameters[0].Value = "News";
parameters[1].Value = "newsID,classID,title,picUrl,videoUrl,writer,source,province,addTime,istop,state,keywords,recommend,hits,htmlPath,pic1,pic2,pic3";
parameters[2].Value = "addTime";
parameters[3].Value = iPageSize;
parameters[4].Value = iPageIndex;
parameters[5].Value = 1;
parameters[6].Value = strWhere;
parameters[7].Direction = ParameterDirection.Output;//声明为输出类型
DataSet ds = sosuo8.DBUtility.DbHelperSQL.RunProcedure("GetRecordByPage", parameters, "News");
iCount = Convert.ToInt32(parameters[7].Value);
return ds;
}

default.aspx.cs代码
复制代码 代码如下:

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
bind();
}
}
private void bind()//绑定数据
{
this.anPager.PageSize = 10;//每页记录数
this.anPager.AlwaysShow = true;//是否一直显示分页
int rowCount = 0;//初始化记录数为0
string wherestr = string.Empty;//搜索关键字,这部分后面我在讲讲优化
sosuo8.BLL.bll_test bll = new sosuo8.BLL.bll_test();
this.rpt.DataSource = bll.GetList(this.anPager.CurrentPageIndex,wherestr,ref rowCount);//rowCount在经过这个函数后,返回的是重新赋值的记录总数
this.anPager.RecordCount = rowCount;
this.rpt.DataBind();
}
protected void anPager_PageChanging(object src, Wuqi.Webdiyer.PageChangingEventArgs e)
{
this.anPager.CurrentPageIndex = e.NewPageIndex;
bind();
}
}

里面涉及到aspnetpager的使用,如果你还不会使用这个控件,可以自己看看有关教程。最终的界面如下:
asp.net安全、实用、简单的大容量存储过程分页第2/2页
令我十分高兴的是,在处理520W的记录时,它速度还在可以接受的范围内,不会出现超时的现象。而作为优化,可以在界面层中尽量少读数据。可以加上以page为参数页面输出缓存:
复制代码 代码如下:

<%@ OutputCache Duration="360" VaryByParam="page" %>

你也许并没有用过里面的一些控件,但是知道原理和方法我相信你也可以*搭配你需要的东西,下面我仅上传部分比较重要的代码,对于需要的控件我也放在里面,至于建表测试那些大家慢慢研究吧!当前的存储过程只能针对一个字段排序,后面有时间我会修改成多字段排序
打包下载地址