随笔-83  评论-551  文章-1  trackbacks-26
存储过程代码如下:
ALTER PROCEDURE [dbo].[Pg_Paging]
@Tables varchar(1000), --表名,多红表是请使用 tA a inner join tB b On a.AID = b.AID
@PK varchar(100),    --主键,可以带表头 a.AID
@Sort varchar(200= ''--排序字段
@PageNumber int = 1,    --开始页码
@PageSize int = 10,        --页大小
@Fields varchar(1000= '*',--读取字段
@Filter varchar(1000= NULL,--Where条件
@Group varchar(1000= NULL,  --分组
@isCount bit = 0     --1    --是否获得总记录数
AS
--
--
select * from GL_NEWS order by GN_UPDATE_DATE DESC
--
exec Pg_Paging @Tables = 'tb_NewsInfo', @PK = 'News_ID', @Sort = 'News_ID DESC', @PageNumber = 2, @PageSize = 15,@Fields = '*', @Group = '', @isCount = 0
DECLARE @strFilter varchar(2000)
declare @sql varchar(8000)
IF @Filter IS NOT NULL AND @Filter != ''
  
BEGIN
   
SET @strFilter = ' WHERE ' + @Filter + ' '
  
END
ELSE
  
BEGIN
   
SET @strFilter = ''
  
END

if @isCount = 1 --只获得记录条数
    begin
        
set @sql = 'SELECT  Count(*) FROM ' + @Tables + @strFilter  
    
end
else
begin
if @Sort = ''
  
set @Sort = @PK + ' DESC '

IF @PageNumber < 1
  
SET @PageNumber = 1

if @PageNumber = 1 --第一页提高性能
begin 
  
set @sql = 'select top ' + str(@PageSize+' '+@Fields+ '  from ' + @Tables + ' ' + @strFilter + ' ORDER BY  '+ @Sort
end 
else
  
begin
  
/**//*Execute dynamic query*/    
   
DECLARE @START_ID varchar(50)
DECLARE @END_ID varchar(50)
SET @START_ID = convert(varchar(50),(@PageNumber - 1* @PageSize + 1)
SET @END_ID = convert(varchar(50),@PageNumber * @PageSize)
    
set @sql =  ' SELECT '+@Fields+ '
   FROM (SELECT ROW_NUMBER() OVER(ORDER BY 
'+@Sort+') AS rownum, 
     
'+@Fields+ '
      FROM 
'+@Tables+') AS D
   WHERE rownum BETWEEN 
'+@START_ID+' AND ' +@END_ID +' ORDER BY '+@Sort
  
END

END
--print @sql

EXEC(@sql)

可以通过封装一个静态函数来执行:(EnterpriseLibrary3。1)

using System;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.Xml;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
namespace Glenet.EjiaShop.SqlData
{
    
/// <summary>
    
///  Pageing
    
/// </summary>
    
public class Pageing
    {
        
public Pageing()
        {
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
        }

        #region Pg_Paging
        
/// <summary>
        
/// Pg_Paging
        
/// </summary>
        
/// <param name="Tables"></param>
        
/// <param name="PK"></param>
        
/// <param name="Filter"></param>
        
/// <returns></returns>
        
public static int Pg_PageCount(string Tables,string PK,string Filter)
        {
            
//创建数据库实例
            
Database db = DatabaseFactory.CreateDatabase();
            
//获得命令
            string sqlCommand 
= "Pg_Paging";
            DbCommand dbCommand 
= db.GetStoredProcCommand(sqlCommand);
            
//设置参数
            db.AddInParameter(dbCommand, "Tables", DbType.String, Tables);
            db.AddInParameter(dbCommand, "PK", DbType.String, PK);
            db.AddInParameter(dbCommand, "Sort", DbType.String, "");
            db.AddInParameter(dbCommand, "PageNumber", DbType.
Double0);
            db.AddInParameter(dbCommand, "PageSize", DbType.
Double0);
            db.AddInParameter(dbCommand, "Fields", DbType.String, "
*");
            db.AddInParameter(dbCommand, "Filter", DbType.String, Filter);
            db.AddInParameter(dbCommand, "
Group", DbType.String, "");
            db.AddInParameter(dbCommand, "isCount", DbType.Boolean, 
1);

            
//执行
            
return int.Parse(db.ExecuteScalar(dbCommand).ToString());    
            
//取得输出参数


        }
        #endregion

        #region Pg_Paging
        
/// <summary>
        
/// Pg_Paging
        
/// </summary>
        
/// <param name="Tables"></param>
        
/// <param name="PK"></param>
        
/// <param name="Sort"></param>
        
/// <param name="PageNumber"></param>
        
/// <param name="PageSize"></param>
        
/// <param name="Fields"></param>
        
/// <param name="Filter"></param>
        
/// <param name="Group"></param>
        
/// <returns></returns>
        
public static DataSet Pg_Paging(string Tables,string PK,string Sort,int PageNumber,int PageSize,string Fields,string Filter,string Group)
        {
            
//创建数据库实例
            
Database db = DatabaseFactory.CreateDatabase();
            
//获得命令
            string sqlCommand 
= "Pg_Paging";
            DbCommand dbCommand 
= db.GetStoredProcCommand(sqlCommand);
            
//设置参数            
            db.AddInParameter(dbCommand, "Tables", DbType.String, Tables);
            db.AddInParameter(dbCommand, "PK", DbType.String, PK);
            db.AddInParameter(dbCommand, "Sort", DbType.String, Sort);
            db.AddInParameter(dbCommand, "PageNumber", DbType.
Double, PageNumber);
            db.AddInParameter(dbCommand, "PageSize", DbType.
Double, PageSize);
            db.AddInParameter(dbCommand, "Fields", DbType.String, Fields);
            db.AddInParameter(dbCommand, "Filter", DbType.String, Filter);
            db.AddInParameter(dbCommand, "
Group", DbType.String, Group);
            db.AddInParameter(dbCommand, "isCount", DbType.Boolean, 
0);

            
//执行
            
return db.ExecuteDataSet(dbCommand);    
        }
        #endregion


    }

}
前台:调用如下:
string filter = "  1 = 1 ";

        AspNetPager1.RecordCount 
= Glenet.EjiaShop.SqlData.Pageing.Pg_PageCount("tb_NewsInfo""News_ID", filter);
        
using (DataSet ds = Glenet.EjiaShop.SqlData.Pageing.Pg_Paging("tb_NewsInfo""News_ID""News_AddTime DESC", AspNetPager1.CurrentPageIndex, AspNetPager1.PageSize, "*", filter, ""))
        
{
            
this.Re_ContentList.DataSource = ds.Tables[0].DefaultView;
            
this.Re_ContentList.DataBind();

            AspNetPager1.CustomInfoText 
= "记录总数:<font color=\"#00007f\"><b>" + AspNetPager1.RecordCount.ToString() + "</b></font>";
            AspNetPager1.CustomInfoText 
+= " 总页数:<font color=\"#00007f\"><b>" + AspNetPager1.PageCount.ToString() + "</b></font>";
            AspNetPager1.CustomInfoText 
+= " 当前页:<font color=\"red\"><b>" + AspNetPager1.CurrentPageIndex.ToString() + "</b></font>";
        }
相当方便。
posted on 2008-01-23 13:41 edobnet 阅读(4281) 评论(50)  编辑 收藏 所属分类: .Net

评论:
#1楼  2008-01-23 13:48 | Argo      
冒昧的问一下那里能够体现高性能?
  回复  引用  查看    
#2楼  2008-01-23 13:51 | 老Q      
俄,我也没看出来,
  回复  引用  查看    
#3楼  2008-01-23 13:57 | 阿不      
注入漏洞看到了吗?
  回复  引用  查看    
#4楼  2008-01-23 14:05 | cslar      
呵!
  回复  引用  查看    
#5楼  2008-01-23 14:24 | Clark Zheng      
使用了ROW_NUMBER() 所以是针对sql2005的优化?可以这么理解吗?
@阿不
是不是说where子句?一个存储过程谈不上漏洞吧,可以在使用时注意一下嘛
  回复  引用  查看    
#6楼  2008-01-23 14:27 | 没剑      
ROW_NUMBER() 就高性能?
楼主是标题党!!!
  回复  引用  查看    
#7楼  2008-01-23 14:29 | kiler      
为什么要在存储过程里面拼装sql,在程序里面拼装sql不是一样的吗。这个存储过程根本就没有存在的意义。
  回复  引用  查看    
#8楼  2008-01-23 14:47 | 捕捉梦想      
学习中.
  回复  引用  查看    
#9楼  2008-01-23 14:58 | 李华星      
这个存储过程有问题吧 当@isCount =1只获取记录条数, 要读去记录必须再执行一次, 其实一次就够了
  回复  引用  查看    
#10楼  2008-01-23 15:03 | 李中华      
鼓励一下。
  回复  引用  查看    
#11楼  2008-01-23 15:11 | 东倒西歪      
--引用--------------------------------------------------
kiler: 为什么要在存储过程里面拼装sql,在程序里面拼装sql不是一样的吗。这个存储过程根本就没有存在的意义。
--------------------------------------------------------
同意他的看法,在存储过程里拼SQL语句,这没有什么好玩的...没意义,我不觉得有什么优化..
  回复  引用  查看    
#12楼  2008-01-23 15:23 | 阿不      
--引用--------------------------------------------------
东倒西歪: --引用--------------------------------------------------
kiler: 为什么要在存储过程里面拼装sql,在程序里面拼装sql不是一样的吗。这个存储过程根本就没有存在的意义。
--------------------------------------------------------
同意他的看法,在存储过程里拼SQL语句,这没有什么好玩的...没意义,我不觉得有什么优化..
--------------------------------------------------------
可能可以拼装,但是拼装后仍必须保持参数化的执行。而不是把参数也拼装进去。
  回复  引用  查看    
#13楼  2008-01-23 15:37 | Argo      
呵呵。看来大家都是奔着高性能来的。而不是本着分页来的。呵呵。
  回复  引用  查看    
#14楼  2008-01-23 16:28 | 孙茹苹      
这个已经不错了,关于返回记录总数和获取相应页面的数据可以一起返回。

上面说的这个存储过程本就没有存在的意义的人,不要当井底之蛙。

楼主发出来这么不错的东西,值得赞扬。
我贴一个一起返回的存储过程。
  回复  引用  查看    
#15楼  2008-01-23 16:29 | 暗香浮动      
之前做过测试。还没有发现比ROW_NUMBER() 更方便高效的分页方法。
就奇怪了mssql数据库怎么就不支持分页功能呢。
  回复  引用  查看    
#16楼  2008-01-23 16:29 | 孙茹苹      
USE [Applelei_DB]
GO
/****** 对象: StoredProcedure [dbo].[UP_GetRecordByPage] 脚本日期: 01/23/2008 16:28:31 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


ALTER PROCEDURE [dbo].[UP_GetRecordByPage]
@tblName varchar(255), -- 表名
@fldName varchar(255), -- 主键或者排序字段名
@PageSize int = 10, -- 页尺寸
@PageIndex int = 1, -- 页码
@IsReCount bit = 0, -- 返回记录总数, 非 0 值则返回
@OrderType bit = 0, -- 设置排序类型, 非 0 值则降序
@strWhere varchar(1000) = '', -- 查询条件 (注意: 不要加 where)
@strSelectField varchar(500) --查询的列(所有列是*,其他是code,name,type)
AS

declare @strSQL varchar(6000) -- 主语句
declare @strTotal varchar(2000) -- 获取记录数合计语句
declare @strTmp varchar(1000) -- 临时变量
declare @strOrder varchar(400) -- 排序类型

if @OrderType != 0
begin
set @strTmp = '<(select min'
set @strOrder = ' order by [' + @fldName +'] desc'
end
else
begin
set @strTmp = '>(select max'
set @strOrder = ' order by [' + @fldName +'] asc'
end

set @strSQL = 'select top ' + str(@PageSize)+ ' ' + @strSelectField + ' 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)+ ' ' + @strSelectField + ' from ['
+ @tblName + '] where [' + @fldName + ']' + @strTmp + '(['
+ @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['
+ @fldName + '] from [' + @tblName + '] where ' + @strWhere + ' '
+ @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder

if @PageIndex = 1
begin
set @strTmp =''
if @strWhere != ''
set @strTmp = ' where ' + @strWhere

set @strSQL = 'select top ' + str(@PageSize)+ ' ' + @strSelectField + ' from ['
+ @tblName + ']' + @strTmp + ' ' + @strOrder

end


if @strWhere != ''
set @strTotal = 'select count(*) as Total from [' + @tblName + ']'+' where ' + @strWhere
else
set @strTotal = 'select count(*) as Total from [' + @tblName + ']'


exec (@strSQL)
if @IsReCount != 0
exec (@strTotal)


  回复  引用  查看    
#17楼  2008-01-23 16:30 | 孙茹苹      
性能优化没有最好,只有更好。

根据个人开发的项目情况,够用就行。
  回复  引用  查看    
#18楼  2008-01-23 16:32 | 孙茹苹      
我贴的这个还有一个特点:可以在程序中完全自定义要返回的字段类型。
完全可以避免返回多余的字段的问题。
  回复  引用  查看    
#19楼  2008-01-23 16:49 | 孙茹苹      
我贴的存储过程有四大优点:
1、可以自定义要返回的字段,避免查询多余的字段数据
2、可以执行一次,同时返回相应页面的数据和查询表的所有记录总数
3、可以适应有主键和没主键的表,可以根据非主键字段进行排序,不过,需要将该字段加上索引,性能有一定的损失。
4、适应3000万以下的数据行,1000万以下的数据查询1s之内返回,3000万的数据大约需要3s。需要看返回的字段和表是否有主键
  回复  引用  查看    
#20楼  2008-01-23 17:24 | Jeffrey Zhao      
这种分页存储过程是肯定要拼接字符串的,但是拼接的方法不应该是这样的。
  回复  引用  查看    
#21楼  2008-01-23 17:34 | 喝小酒的网摘 [未注册用户]
收藏...
  回复  引用    
#22楼  2008-01-23 18:02 | ytzong      
@孙茹苹
我试了一下你的存储过程,根据非主键字段排序有问题啊
  回复  引用  查看    
#23楼  2008-01-23 18:21 | JuzzPig [未注册用户]
如果仅使用ROW_NUMBER就算是高性能方案的话,那么LINQ的分页无疑是最高的。楼主可以观察linq分页时自动生成的SQL语句,与你的如出一则
  回复  引用    
#24楼  2008-01-23 18:31 | 江南白衣      
上千W的表用Count(*) ??
  回复  引用  查看    
#25楼  2008-01-23 19:43 | jillzhang      
@没剑
多亏您没生在文化大革命时期,要不大家还不都给你打成走私派,黑干将亚,:)
  回复  引用  查看    
#26楼  2008-01-23 23:49 | PerfectDesign      
这个也叫高性能啊?
连起码的安全都没有顾及到。防基本的注入都没有
另外动态SQL在执行计划里一般是不予缓存的,也就是需要每次都要耗费大量的CPU来编译存储过程
sql很多,很辛苦。
可以玩玩我这个sql
http://www.cnblogs.com/perfectdesign/archive/2008/01/15/gernerateADOCode.html

  回复  引用  查看    
#27楼  2008-01-24 00:27 | kiler      
@孙茹苹
你没搞清楚我的意思,我的意思这个存储过程仅仅只是做了一个拼凑sql的动作,用存储过程写和用c#写是没有区别的,既然如此何必写个存储过程,lz在Pg_Paging和Pg_PageCount方法里面把sql一拼,再执行一下不就完了,还写存储过程干什么?

if @PageIndex = 1
begin
set @strTmp =''
if @strWhere != ''
set @strTmp = ' where ' + @strWhere
set @strSQL = 'select top ' + str(@PageSize)+ ' ' + @strSelectField + ' from ['
+ @tblName + ']' + @strTmp + ' ' + @strOrder
end

这样的东西和
if(PageIndex ==1)
{
strTmp ="";
if(strWhere != "")
strTmp = " where " + strWhere;

strSQL = "select top " + PageSize+ " "+ strSelectField + " from ["
+ tblName + "]" + strTmp + " " + strOrder
}

有什么不同。




  回复  引用  查看    
#28楼  2008-01-24 00:39 | 没剑      
@江南白衣
上千W的表用Count(*) ??
实践证明Count(*) 很耗性能!!!无论索引多好

--引用--------------------------------------------------
jillzhang: @没剑
多亏您没生在文化大革命时期,要不大家还不都给你打成走私派,黑干将亚,:)
--------------------------------------------------------
汗,偶是不是给MS斗啊?因为偶说他的“ROW_NUMBER() 就高性能?”
555.。。
  回复  引用  查看    
#29楼  2008-01-24 07:31 | kiler      
@没剑

Count(*) 耗不耗性能因数据库而异。oracle貌似是更耗性能。sqlserver一般情况下应该是一样的。

http://www.qiter.com/blog/article.asp?id=228
http://www.itpub.net/344078.html

性能优化这块没有绝对的真理,只有实践结果才是对的。


  回复  引用  查看    
#30楼  2008-01-24 09:08 | pengyuan      
一直是用的这样的分页方法……这种方法的确是目前我在SQL SERVER 2005下发现分页效率最高
第一页用top,后面的用ROW_NUMBER
楼主应该是介绍这种方法吧

支持~~~

还有count(*)在SQL SERVER下的确和count(ID)没啥区别。做过实验的……因为原来也是被以前一个项目组长说我这么做不行……结果我就做了个实验DEMO发给他,嘿嘿
  回复  引用  查看    
#31楼  2008-01-24 09:12 | kevin.xiaoli [未注册用户]
都为了性能来看这个存储过程的,对于说这个存储过程不好的同志,请你们把你们的分页存储过程拿出来分享一下啊;对于注入问题,我想在程序中把查询条件做为参数前就已经对其进行了过滤了吧,为什么还有人说在sql语句中没有防注入呢;个人观点.
  回复  引用    
#32楼  2008-01-24 09:38 | wdwbto [未注册用户]
真正的高性能,通用的存储过程是不行的。必须根据库的需求而定,而且拼接并不是好的方案,你的存储过程适合于200w以下基本没问题
  回复  引用    
#33楼  2008-01-24 10:08 | PerfectDesign      
sqlserver的防注入安全保证不是由危险字符过滤来实现的,穷举过滤只会带来更多的字符查找替换,而且你能确保你已经穷举尽了?
  回复  引用  查看    
#34楼  2008-01-24 10:51 | 没剑      
--引用--------------------------------------------------
pengyuan: 一直是用的这样的分页方法……这种方法的确是目前我在SQL SERVER 2005下发现分页效率最高
第一页用top,后面的用ROW_NUMBER
楼主应该是介绍这种方法吧

支持~~~

还有count(*)在SQL SERVER下的确和count(ID)没啥区别。做过实验的……因为原来也是被以前一个项目组长说我这么做不行……结果我就做了个实验DEMO发给他,嘿嘿
--------------------------------------------------------
count(*)和count(ID)确实没有区别,因为count(*)会自动搜索最快的索引

  回复  引用  查看    
#35楼  2008-01-24 10:52 | 没剑      
--引用--------------------------------------------------
kiler: @没剑

Count(*) 耗不耗性能因数据库而异。oracle貌似是更耗性能。sqlserver一般情况下应该是一样的。

http://www.qiter.com/blog/article.asp?id=228
http://www.itpub.net/344078.html

性能优化这块没有绝对的真理,只有实践结果才是对的。
----
偶没有用过oracle,只用过mssql,所以偶的发言权也只限于mssql

  回复  引用  查看    
#36楼  2008-01-24 10:58 | 没剑      
--引用--------------------------------------------------
孙茹苹: 我贴的存储过程有四大优点:
1、可以自定义要返回的字段,避免查询多余的字段数据
2、可以执行一次,同时返回相应页面的数据和查询表的所有记录总数
3、可以适应有主键和没主键的表,可以根据非主键字段进行排序,不过,需要将该字段加上索引,性能有一定的损失。
4、适应3000万以下的数据行,1000万以下的数据查询1s之内返回,3000万的数据大约需要3s。需要看返回的字段和表是否有主键
--------------------------------------------------------
楼主的意见偶不敢苟同,楼主第4条优点,感觉只是在没有where条件,或者只有一个where条件(主键),或者没有orderby或者只有一个orderby(主键)条件下测出来的吧,实际使用中根本或者很少没有orderby或者where条件的,最简单的一个文章发布系统的话如选择相关分类,按照发布时间/点击数等排序的。。。
我在实际使用ROW_NUMBER() 中可得知,ROW_NUMBER() 性能确实是不错,但对分页没有很大的帮忙,我们唯一要做的是最大限度的限制返回的数据,如不要针对1000W以上的数据进行分页,因为根本就没有必要,谁会所有页都翻下去?
实际测试过,在100W数据以下,用多条件,多排序情况下ROW_NUMBER() 的表现还是很不错的。
  回复  引用  查看    
#37楼  2008-01-24 11:28 | kiler      
ROW_NUMBER()的性能也是有人测过,据说内部实现用的是临时表,性能只能算是中等,其实分页性能这个东西没必要太在意了,几kw的数据直接拿出来翻页本身就是一种不好的设计,客户真的愿意把这些数据从头看到尾?
  回复  引用  查看    
#38楼  2008-01-24 12:41 | 阿瑞--16hi      
同意kiler说的,数据量大了之后就应该走子表,这样数据量得到控制,效率可能更理想!!
  回复  引用  查看    
#39楼  2008-01-24 13:03 | 东倒西歪      
像这种分页的存储过程不应该用这种通用的,应该针对具体的表来写,那样就不用拼SQL语句了,一般都使用内表.
  回复  引用  查看    
#40楼  2008-01-24 16:55 | zhuyong      
在SQL中这样子来拼装SQL语句,速度会很慢的哟
  回复  引用  查看    
#41楼  2008-01-24 17:16 | 鼠标王 [未注册用户]
游戏人偏激了。
看来回复这个技术牛人比较多(呵呵),一个项目的开发时间是有限制的,要在时间和性能方面有一个综合的考虑,什么都根据具体表来写,性能肯定不会差,但时间呢?
就这样吧,大家别回复了。已经没什么意义了,说存储过程好的也都用上了,说不好的你也不会用。
别争论啦。
  回复  引用    
#42楼  2008-01-24 23:14 | Argo      
我来收个尾,我开的头,还是我来结尾吧。
总之楼主这个帖子是好贴,无论如何我们讨论了高性能存储过程。我引用鼠标王一句话

---------------------------------------
说存储过程好的也都用上了,说不好的你也不会用。
  回复  引用