一个多对多关系表的数据分页显示问题-sp_cursoropen 比较 临时表方式
2009-06-06 11:06
567 查看
'=============================
各种分页存储过程谁好谁坏,其实没有绝对,而各类存储过程快慢表现的差别,其实质是查询过程中能否有效果利用索引的差别,尤其是聚集索引的利用
'=============================
一个新闻模块,需要专题功能, 文章与专题是多对多关系,表数据结构如果下:
文章表:Article(Id,ClassId,Title....)
专题表:Special(SpecialId,Title,Url...)
文章专题表:ArticleSpecial(ArticleId,SpecialId,SectionId,OrderId) -- SectionId版块,
管理后台需要如下分页列表:
文章编号 文章标题 专题名称 版块号 排序号
对应:R(Article.Id,Article.Title,Special.Title,ArticleSpecial.SectionId,ArticleSpecial.OrderId)
另外在Asp.net 中采用ObjectDataSource + GridView , GridView中需要支持ArticleSpecial表的OrderId,SectionId,ArticleId等字段排序显示(每次只允许一个).
=============================================
目前在MSSQL2000下使用的分页存储过程基本就那么几个, 由于这里设计关系表(ArticleSpecial)是使用复合主键,应次Select Max(key) 方式, Set Rowcount 等方式不能用, 只能使用插入临时表方式跟服务器端游标(使用sp_cursoropen 等)方式,---注意这里不考虑网ArticleSpecial表中加入额外主键,加入额外主键后,分页管理到是可以方便解决,但是考虑上面三个表inner join 操作的性能问题故把主键(默认设置为聚集索引)分配给ArticleSpecial表的 SpecialD跟ArticleId列.
sp_cursoropen这些游标操作API,虽然在MS的帮助文档里找不到,但是如果使用asp + ADO 进行数据库操作时,其实可以看到(SQL跟踪)ADO调用的也就是这些游标操作API.
===============================
--//游标分页存储过程
ALTER procedure QueryByCursor
@sqlstr nvarchar(4000), --查询字符串
@StartRows int, --第N页
@pageSize int, --每页行数
@Total int OUTPUT
As
Set nocount on
Declare @P1 int --P1是游标的id
If @StartRows<=0
Begin
Set @StartRows=1
End
Else
Begin
Set @StartRows=@StartRows
End
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@Total=@Total output
exec sp_cursorfetch @P1,16,@startRows,@pagesize
exec sp_cursorclose @P1
Return @Total
Set NoCount off
//-- asp.net 中对应的操作
//1.帮助函数,对QueryByCursor进行封装,注意这里反回的是第二个Table,第一个为空
public static DataSet Query(string connStr,int startRows,int pageSize,string sql,out int total)
{
SqlParameter sqlTotal = new SqlParameter("@total", 0);
total = 0;
sqlTotal.Direction = ParameterDirection.Output;
SqlParameter[] parameters ={
new SqlParameter("@startRows",startRows),
new SqlParameter("@pageSize",pageSize),
new SqlParameter("@sqlstr",sql),
sqlTotal
};
DataSet ds = SqlHelper.ExecuteDataset(connStr, CommandType.StoredProcedure, "QueryByCursor", parameters);
if (ds.Tables.Count >= 2)
{
ds.Tables.RemoveAt(0); //第一个为空
}
total = Convert.ToInt32(sqlTotal.Value);
return ds;
}
//过滤以及一些拼接操作,最后调用上面的帮助函数Query(...)
public static DataSet SelectByCursor(int startRowIndex, int maximumRows, int specialId, string key, string sortExpress)
{
key = StringHelper.FilterTSQL(key);
sortExpress = StringHelper.FilterTSQL(sortExpress);
string sql = @" Select a2s.*,a.Title,s.Title as SpecialName
From
ArticleSpecial a2s
inner join
Article a on a.Id=a2s.ArticleId
Inner join
Special s On s.SpecialId=a2s.SpecialId ";
string where = "Where 1=1 ";
string order = "";
if (specialId > 0)
{
where += " And a2s.SpecialId=" + specialId;
}
if (!string.IsNullOrEmpty(key))
{
where += " And (a.Title like '%" + key + "%' Or a.Keys like '%" + key + "%')";
}
if (!string.IsNullOrEmpty(sortExpress))
{
order = " Order By a2s." + sortExpress;
}
sql += where + order;
return DBH.Query(DBH.NewsDB, startRowIndex, maximumRows, sql, out _RecordCount);
}
========================================
使用插入临时表的方式:
//--存储过程入下:
---根据指定条件返回分页后的文章专题数据
---第二节的多个inner join 语句始终使用 t开始
ALTER Proc ArticleSpecial_Query
@starRows int =0,
@pageSize int =20,
@SpecialId int,
@Key nvarchar(100),
@OrderColumn nvarchar(50)
As
Declare @SQL nvarchar(4000)
Declare @where nvarchar(200)
Declare @order nvarchar
Set @where=' 1=1 '
--专题不为0
If @specialId>0
Begin
Set @Where=@Where + ' And a2s.SpecialId=' + cast(@SpecialId as nvarchar(50))
End
--关键字不为空
If @Key !=''
Begin
Set @where=@where + ' And (a.Title like ''%' +@key +'%'' Or a.Keys like ''%' + @key + '%'')'
End
If @OrderColumn=''
Begin
Set @OrderColumn='ArticleId'
End
Set @SQL='
Set Nocount ON
Create table #t (nid bigint identity(1,1) Primary Key,ArticleID int,SpecialID int)
Insert Into #t(ArticleId,SpecialId)
Select a2s.ArticleId,a2s.SpecialId
From
ArticleSpecial a2s inner join Article a on a.Id=a2s.ArticleId
Where '+ @where +'
Order By a2s.' + @OrderColumn +'
Select a2s.*,a.Title,s.Title as SpecialName
From
ArticleSpecial a2s
inner join
Article a on a.Id=a2s.ArticleId
Inner join
Special s On s.SpecialId=a2s.SpecialId
Inner join [#t] t
On a2s.ArticleID=t.ArticleID And t.SpecialID=a2s.SpecialID
Where nid >' + cast(@starRows as nvarchar) +' and nid <= ' + cast( (@starRows +@pageSize) as nvarchar) +'
Set NoCount Off
Drop Table #t
'
Exec(@SQL
=======================================
//测试代码: Article表中有10万条数据, Special表中有200表记录(200个专题),ArticleSpecial表中有1万2千条记录,
//这个数据级别基本符合公司5到10年的需求了
private delegate DataSet TestFunction(int startIndex, int pageSize, int specialId, string key, string sortExpress);
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
TestFunction fun1 = new TestFunction(ArticleSpecialDAL.Select);
TestFunction fun2 = new TestFunction(ArticleSpecialDAL.SelectByCursor);
Stopwatch sw1 = new Stopwatch();
Stopwatch sw2 = new Stopwatch();
#region 方式1
sw1.Start();
for (int i = 0; i < 12000; i = i + 500)
{
DataSet ds = DoFunction(fun1, i, 20, 0, "", "ArticleId");
}
sw1.Stop();
#endregion
#region 方式2
sw2.Start();
for (int i = 0; i < 12000; i = i + 500)
{
DataSet ds = DoFunction(fun2, i, 20, 0, "", "ArticleId");
}
sw2.Stop();
#endregion
label1.Text = "Select方式:" + sw1.ElapsedMilliseconds.ToString() +Environment.NewLine
+ "SelectByCursor:" + sw2.ElapsedMilliseconds.ToString();
button1.Enabled = true;
}
private DataSet DoFunction(TestFunction fun,int startIndex,int pageSize ,int specialId ,string key ,string sortExpress)
{
return fun(startIndex, pageSize, specialId, key, sortExpress);
}
//============================
测试结果:
使用服务器游标方式比使用插入临时表方式快了将近10倍,插入临时表方式完成上面操作一般需要2万毫秒,而使用游标方式只需要2千毫秒,当然网上经常可以看到的说法是游标方式比插入临时表方式要慢, 这个主要是大家面对的问题不同,数据量也不同,我上面的测试结果是在ArticleSpecial表有12000条记录下得出的,当数据量继续整加时是什么情况我没做测试.
另外发现当传入key关键字跟key保持为空时,插入临时表方式运行时间差不多,而使用游标方式者区别很到,另外两种方式的CUP站用率都比为空时高很多(我这达到50%,为空时10%),可见字符比较操作占用大量的cup操作时间
各种分页存储过程谁好谁坏,其实没有绝对,而各类存储过程快慢表现的差别,其实质是查询过程中能否有效果利用索引的差别,尤其是聚集索引的利用
'=============================
一个新闻模块,需要专题功能, 文章与专题是多对多关系,表数据结构如果下:
文章表:Article(Id,ClassId,Title....)
专题表:Special(SpecialId,Title,Url...)
文章专题表:ArticleSpecial(ArticleId,SpecialId,SectionId,OrderId) -- SectionId版块,
管理后台需要如下分页列表:
文章编号 文章标题 专题名称 版块号 排序号
对应:R(Article.Id,Article.Title,Special.Title,ArticleSpecial.SectionId,ArticleSpecial.OrderId)
另外在Asp.net 中采用ObjectDataSource + GridView , GridView中需要支持ArticleSpecial表的OrderId,SectionId,ArticleId等字段排序显示(每次只允许一个).
=============================================
目前在MSSQL2000下使用的分页存储过程基本就那么几个, 由于这里设计关系表(ArticleSpecial)是使用复合主键,应次Select Max(key) 方式, Set Rowcount 等方式不能用, 只能使用插入临时表方式跟服务器端游标(使用sp_cursoropen 等)方式,---注意这里不考虑网ArticleSpecial表中加入额外主键,加入额外主键后,分页管理到是可以方便解决,但是考虑上面三个表inner join 操作的性能问题故把主键(默认设置为聚集索引)分配给ArticleSpecial表的 SpecialD跟ArticleId列.
sp_cursoropen这些游标操作API,虽然在MS的帮助文档里找不到,但是如果使用asp + ADO 进行数据库操作时,其实可以看到(SQL跟踪)ADO调用的也就是这些游标操作API.
===============================
--//游标分页存储过程
ALTER procedure QueryByCursor
@sqlstr nvarchar(4000), --查询字符串
@StartRows int, --第N页
@pageSize int, --每页行数
@Total int OUTPUT
As
Set nocount on
Declare @P1 int --P1是游标的id
If @StartRows<=0
Begin
Set @StartRows=1
End
Else
Begin
Set @StartRows=@StartRows
End
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@Total=@Total output
exec sp_cursorfetch @P1,16,@startRows,@pagesize
exec sp_cursorclose @P1
Return @Total
Set NoCount off
//-- asp.net 中对应的操作
//1.帮助函数,对QueryByCursor进行封装,注意这里反回的是第二个Table,第一个为空
public static DataSet Query(string connStr,int startRows,int pageSize,string sql,out int total)
{
SqlParameter sqlTotal = new SqlParameter("@total", 0);
total = 0;
sqlTotal.Direction = ParameterDirection.Output;
SqlParameter[] parameters ={
new SqlParameter("@startRows",startRows),
new SqlParameter("@pageSize",pageSize),
new SqlParameter("@sqlstr",sql),
sqlTotal
};
DataSet ds = SqlHelper.ExecuteDataset(connStr, CommandType.StoredProcedure, "QueryByCursor", parameters);
if (ds.Tables.Count >= 2)
{
ds.Tables.RemoveAt(0); //第一个为空
}
total = Convert.ToInt32(sqlTotal.Value);
return ds;
}
//过滤以及一些拼接操作,最后调用上面的帮助函数Query(...)
public static DataSet SelectByCursor(int startRowIndex, int maximumRows, int specialId, string key, string sortExpress)
{
key = StringHelper.FilterTSQL(key);
sortExpress = StringHelper.FilterTSQL(sortExpress);
string sql = @" Select a2s.*,a.Title,s.Title as SpecialName
From
ArticleSpecial a2s
inner join
Article a on a.Id=a2s.ArticleId
Inner join
Special s On s.SpecialId=a2s.SpecialId ";
string where = "Where 1=1 ";
string order = "";
if (specialId > 0)
{
where += " And a2s.SpecialId=" + specialId;
}
if (!string.IsNullOrEmpty(key))
{
where += " And (a.Title like '%" + key + "%' Or a.Keys like '%" + key + "%')";
}
if (!string.IsNullOrEmpty(sortExpress))
{
order = " Order By a2s." + sortExpress;
}
sql += where + order;
return DBH.Query(DBH.NewsDB, startRowIndex, maximumRows, sql, out _RecordCount);
}
========================================
使用插入临时表的方式:
//--存储过程入下:
---根据指定条件返回分页后的文章专题数据
---第二节的多个inner join 语句始终使用 t开始
ALTER Proc ArticleSpecial_Query
@starRows int =0,
@pageSize int =20,
@SpecialId int,
@Key nvarchar(100),
@OrderColumn nvarchar(50)
As
Declare @SQL nvarchar(4000)
Declare @where nvarchar(200)
Declare @order nvarchar
Set @where=' 1=1 '
--专题不为0
If @specialId>0
Begin
Set @Where=@Where + ' And a2s.SpecialId=' + cast(@SpecialId as nvarchar(50))
End
--关键字不为空
If @Key !=''
Begin
Set @where=@where + ' And (a.Title like ''%' +@key +'%'' Or a.Keys like ''%' + @key + '%'')'
End
If @OrderColumn=''
Begin
Set @OrderColumn='ArticleId'
End
Set @SQL='
Set Nocount ON
Create table #t (nid bigint identity(1,1) Primary Key,ArticleID int,SpecialID int)
Insert Into #t(ArticleId,SpecialId)
Select a2s.ArticleId,a2s.SpecialId
From
ArticleSpecial a2s inner join Article a on a.Id=a2s.ArticleId
Where '+ @where +'
Order By a2s.' + @OrderColumn +'
Select a2s.*,a.Title,s.Title as SpecialName
From
ArticleSpecial a2s
inner join
Article a on a.Id=a2s.ArticleId
Inner join
Special s On s.SpecialId=a2s.SpecialId
Inner join [#t] t
On a2s.ArticleID=t.ArticleID And t.SpecialID=a2s.SpecialID
Where nid >' + cast(@starRows as nvarchar) +' and nid <= ' + cast( (@starRows +@pageSize) as nvarchar) +'
Set NoCount Off
Drop Table #t
'
Exec(@SQL
=======================================
//测试代码: Article表中有10万条数据, Special表中有200表记录(200个专题),ArticleSpecial表中有1万2千条记录,
//这个数据级别基本符合公司5到10年的需求了
private delegate DataSet TestFunction(int startIndex, int pageSize, int specialId, string key, string sortExpress);
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
TestFunction fun1 = new TestFunction(ArticleSpecialDAL.Select);
TestFunction fun2 = new TestFunction(ArticleSpecialDAL.SelectByCursor);
Stopwatch sw1 = new Stopwatch();
Stopwatch sw2 = new Stopwatch();
#region 方式1
sw1.Start();
for (int i = 0; i < 12000; i = i + 500)
{
DataSet ds = DoFunction(fun1, i, 20, 0, "", "ArticleId");
}
sw1.Stop();
#endregion
#region 方式2
sw2.Start();
for (int i = 0; i < 12000; i = i + 500)
{
DataSet ds = DoFunction(fun2, i, 20, 0, "", "ArticleId");
}
sw2.Stop();
#endregion
label1.Text = "Select方式:" + sw1.ElapsedMilliseconds.ToString() +Environment.NewLine
+ "SelectByCursor:" + sw2.ElapsedMilliseconds.ToString();
button1.Enabled = true;
}
private DataSet DoFunction(TestFunction fun,int startIndex,int pageSize ,int specialId ,string key ,string sortExpress)
{
return fun(startIndex, pageSize, specialId, key, sortExpress);
}
//============================
测试结果:
使用服务器游标方式比使用插入临时表方式快了将近10倍,插入临时表方式完成上面操作一般需要2万毫秒,而使用游标方式只需要2千毫秒,当然网上经常可以看到的说法是游标方式比插入临时表方式要慢, 这个主要是大家面对的问题不同,数据量也不同,我上面的测试结果是在ArticleSpecial表有12000条记录下得出的,当数据量继续整加时是什么情况我没做测试.
另外发现当传入key关键字跟key保持为空时,插入临时表方式运行时间差不多,而使用游标方式者区别很到,另外两种方式的CUP站用率都比为空时高很多(我这达到50%,为空时10%),可见字符比较操作占用大量的cup操作时间
相关文章推荐
- 建站或者网站搬家换空间的时候,企业站长最关心的一个问题是该如何选择网站空间,而这一问题对于一些擅长的站长来说非常小意思,但对于部分企业站长来说是一个比较头疼的问题。根据不完整数据显示,很多企业站长因为
- datalist 的用法。也是增删改查,但是比较智能。用数据绑定的方式,可以有不同的显示方法,下面是对一个表的增删改查的参考代码
- asp.net mvc jqgrid 同一个页面查询不同的表,jqgrid显示不同表的表头和数据并且分页
- 处理大数据分页下拉列表显示方式
- Java连接HBASE数据库,创建一个表,删除一张表,修改表,输出插入,修改,数据删除,数据获取,显示表信息,过滤查询,分页查询,地理hash
- 对于分页时,若数据库的数据不断更新,不让前台显示脏数据(同一条数据重复显示)的处理方式
- Python——集合是一个非常之牛逼的数据比较方式
- bootstrap datatable显示数据表格及因分页获取数据造成的搜索问题
- 【用jsp进行数据分页显示的一个实现】
- 针对这一段时间ASP.NET版中比较集中突出的问题,我写了一个完整的页面,包含显示/修改/删除/添加/排序/合并/写文件/显示图片或文档,有详细代码注释
- 页面中iframe中嵌入一个跨域的页面,让这个页面按照嵌入的页面宽高大小显示的方式;iframe嵌套的页面不可以编辑的问题解决方案
- 今天在处理GridView分页问题时遇到了一个比较常见的分页出错问题。
- iOS不得姐项目--推荐关注模块(一个控制器控制两个tableView),数据重复请求的问题,分页数据的加载,上拉下拉刷新(MJRefresh)
- 最近写的一个分页数据显示及分页导航
- 页面中iframe中嵌入一个跨域的页面,让这个页面按照嵌入的页面宽高大小显示的方式;iframe嵌套的页面不可以编辑的问题解决方案
- 腾讯云图片鉴黄集成到C# SQL Server 怎么在分页获取数据的同时获取到总记录数 sqlserver 操作数据表语句模板 .NET MVC后台发送post请求 百度api查询多个地址的经纬度的问题 try{}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code 会 不会被执行,什么时候被执行,在 return 前还是后? js获取某个日期
- 一个用存储过程实现数据分页显示的例子,有更合适的请朋友们跟帖啊
- 用jsp进行数据分页显示的一个实现
- 当web应用中面临大数据量同时并发量比较大的情况下性能是一个尤为重要的问题,面对性能优化我们应从何做起,在哪些方面做优化呢?
- UltraWebGrid在不分页的情况下默认只能显示50条数据的问题