|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
无论谁倒了对双方阵营的粉丝们也是有害无益。asp.net|分页 在Web使用程序中,对一个年夜数据库了局集举行分页已是一个众所周知的成绩了。复杂的说,你不但愿一切的查询数据显现在一个独自的页面中,以是带有分页的显现才是更符合的。固然在传统的asp里这并非一个复杂的义务,但在asp.net中,DataGrid控件把这一历程简化为只要几行代码。因而,在asp.net中,分页很复杂,可是默许的DataGrid分页事务会从数据库中把一切的纪录全体读出来放到asp.netweb使用程序中。当你的数据在一百万以上的时分,这将引发严峻的功能成绩(假如你不信任,你能够在你的使用程序中实行一个查询,然后在义务办理器中检察aspnet_wp.exe的内存损耗情形)这也就是为何必要自界说分页举动,如许能够包管仅取得以后页必要的数据纪录。
在网上有良多关于这个成绩的文章和帖子,另有一些成熟的办理计划。我写这篇文章的目标不是向你展现一个能够办理统统成绩的存储历程,而是出于优化已无方法,同时为你供应一个可供测试的使用程序,如许你就能够依据本人的必要举行开辟。
可是我对今朝网上先容的办法不是很中意。第一,利用了传统的ADO,很分明它们是为“陈旧”的asp而写的。剩下的一些办法就是SQLServer存储历程,而且个中的一些因为响应工夫过慢而没法利用,正如你在文章最初所看到的功能了局一样,可是仍是有一些引发了我的注重。
通用化
我要对对今朝经常使用的三个办法举行细心的剖析,它们是一时表(TempTable),静态SQL(DynamicSQL)和行计数(Rowcount)。鄙人文中,我更乐意把第二个办法称为(升序-降序)Asc-Desc办法。我不以为静态SQL是一个好名字,由于你也能够把静态SQL逻辑使用于另外一个办法中。一切这些存储历程的通病在于,你不能不估量哪些列是你行将要排序的,而不单单是估量主键列(PKColumns)罢了,这大概招致一系列的成绩――关于每一个查询来讲,你必要经由过程分页显现,也就是说关于每分歧的排序列你必需有很多分歧的分页查询,这意味着你要末给每一个排序列做分歧的存储历程(不管利用哪一种分页办法),也么你必需借助静态SQL的匡助把这个功效放在一个存储过程当中。这两个办法关于功能有巨大的影响,可是它增添了可保护性,出格是当你必要利用这个办法显现分歧的查询。因而,在本文中我会实验利用静态SQL对一切的存储历程举行归结,可是因为一些缘故原由,我们只能对完成部分的通用性,因而你仍是得为庞大查询写自力的存储历程。
同意包含主键列在内的一切排序字段的第二个成绩在于,假如那些列没有作得当的索引,那末这些办法一个也帮不上忙。在一切这些办法中,关于一个分页源必需先做排序,关于年夜数据表来讲,利用非索引列排序的本钱是能够疏忽不计的。在这类情形下,因为响应工夫太长,一切的存储历程都是没法在实践情形下利用的。(响应的工夫各有分歧,从几秒钟到几分钟不等,这要依据表的巨细和所要取得的第一个纪录而定)。其他列的索引会带来分外的不但愿呈现的功能成绩,比方假如你天天的导进数据良多,它有大概变得很慢。
一时表
起首,我筹办先来讲一下一时表办法,这是一个普遍被倡议利用的办理计划,我在项目中碰到过好几回了。上面让我们来看看这个办法的本色:
CREATETABLE#Temp(
IDintIDENTITYPRIMARYKEY,
PK/*heregoesPKtype*/
)
INSERTINTO#TempSELECTPKFROMTableORDERBYSortColumn
SELECTFROMTableJOIN#TemptempONTable.PK=temp.PKORDERBYtemp.IDWHEREID>@StartRowANDID<@EndRow
经由过程把一切的行拷贝光临时表中,我们能够对查询进一步的优化(SELECTTOPEndRow…),可是关头在于最坏情形――一个包括100万纪录的表就会发生一个100万笔记录的一时表。思索到如许的情形,再看看下面文章的了局,我决意在我的测试中保持该办法
升序-降序
这个办法在子查询中利用默许排序,在主查询中利用反向排序,道理是如许的:
DECLARE@tempTABLE(
PK/*PKType*/
NOTNULLPRIMARY
)
INSERTINTO@tempSELECTTOP@PageSizePKFROM
(
SELECTTOP(@StartRow+@PageSize)
PK,
SortColumn/*IfsortingcolumnisdefferentfromthePK,SortColumnmust
befetchedaswell,otherwisejustthePKisnecessary
*/
ORDERBYSortColumn
/*
defaultorderCtypicallyASC
*/
)
ORDERBYSortColumn
/*
reversed default orderCtypicallyDESC
*/
SELECTFROMTableJOIN@TemptempONTable .PK=temp.PK
ORDERBYSortColumn
/*
defaultorder
*/
行计数
这个办法的基础逻辑依附于SQL中的SETROWCOUNT表达式,如许能够跳过不用要的行而且取得必要的行纪录:
DECLARE@Sort/*thetypeofthesortingcolumn*/
SETROWCOUNT@StartRow
SELECT@Sort=SortColumnFROMTableORDERBYSortColumn
SETROWCOUNT@PageSize
SELECTFROMTableWHERESortColumn>=@SortORDERBYSortColumn
子查询
另有两个办法也是我思索过的,他们的来历分歧。第一个是尽人皆知的三角查询(TripleQuery)大概说自查询办法,在本文中,我也用一个相似的包括一切其他存储历程的通用逻辑。这里的道理是毗连到全部过程当中,我对原始代码做了一些缩减,由于recordcount在我的测试中不必要)
SELECTFROMTableWHEREPKIN(
SELECTTOP@PageSizePKFROMTableWHEREPKNOTIN
(
SELECTTOP@StartRowPKFROMTableORDERBYSortColumn)
ORDERBYSortColumn)
ORDERBYSortColumn
游标
在看google会商组的时分,我找到了最初一个办法。该办法是用了一个服务器端静态游标。很多人试图制止利用游标,由于游标没有干系可言,和有序性招致其效力不高,但回过火来看,分页实际上是一个有序的义务,不管你利用哪一种办法,你都必需回到入手下手行纪录。在之前的办法中,先选择一切在入手下手纪录之前的一切行,加上必要的行纪录,然后删除一切之前的行。静态游标有一个FETCHRELATIVE选项能够完成邪术般的跳转。基础的逻辑以下:
DECLARE@PK/*PKType*/
DECLARE@tblPK
TABLE(
PK /*PKType*/NOTNULLPRIMARYKEY
)
DECLAREPagingCursorCURSORDYNAMICREAD_ONLYFOR
SELECT@PKFROMTableORDERBYSortColumn
OPENPagingCursor
FETCHRELATIVE@StartRowFROMPagingCursorINTO@PK
WHILE@PageSize>0AND@@FETCH_STATUS=0
BEGIN
INSERT@tblPK(PK)VALUES(@PK)
FETCHNEXTFROMPagingCursorINTO@PK
SET@PageSize=@PageSize-1
END
CLOSE
PagingCursor
DEALLOCATE
PagingCursor
SELECTFROMTableJOIN@tblPKtempONTable.PK=temp.PK
ORDERBYSortColumn 庞大查询的通用化
我在之前指出,一切的存储历程都是用静态SQL完成通用性的,因而,实际上它们能够用任何品种的庞大查询。上面有一个基于Northwind数据库的庞大查询例子。
SELECTCustomers.ContactNameASCustomer,Customers.Address+,+Customers.City+,+Customers.Country
ASAddress,SUM([OrderDetails].UnitPrice*[OrderDetails].Quantity)
AS[Totalmoneyspent]
FROMCustomers
INNERJOINOrdersONCustomers.CustomerID=Orders.CustomerID
INNERJOIN[OrderDetails]ONOrders.OrderID=[OrderDetails].OrderID
WHERECustomers.CountryUSAANDCustomers.CountryMexico
GROUPBYCustomers.ContactName,Customers.Address,Customers.City,Customers.Country
HAVING(SUM([OrderDetails].UnitPrice*[OrderDetails].Quantity))>1000
ORDERBYCustomerDESC,AddressDESC
前往第二个页面的分页存储挪用以下:
EXECProcedureName
/*Tables*/
Customers
INNERJOINOrdersONCustomers.CustomerID=Orders.CustomerID
INNERJOIN[OrderDetails]ONOrders.OrderID=[OrderDetails].OrderID
,
/*PK*/
Customers.CustomerID
,
/*ORDERBY*/
Customers.ContactNameDESC,Customers.AddressDESC
,
/*PageNumber*/
2
,
/*PageSize*/
10
,
/*Fields*/
Customers.ContactNameASCustomer,
Customers.Address+,+Customers.City+,+Customers.CountryASAddress,SUM([OrderDetails].UnitPrice*[OrderDetails].Quantity)AS[Totalmoneyspent]
,
/*Filter*/
Customers.CountryUSAANDCustomers.CountryMexico,
/*GroupBy*/
Customers.CustomerID,Customers.ContactName,Customers.Address,
Customers.City,Customers.Country
HAVING(SUM([OrderDetails].UnitPrice*[OrderDetails].Quantity))>1000
值得注重的是,在原始查询中在ORDERBY语句中利用了别号,但你最好不要在分页存储过程当中这么做,由于如许的话跳过入手下手纪录之前的行是很损耗工夫的。实在有良多种办法能够用于完成,但准绳是不要在一入手下手把一切的字段包含出来,而仅仅是包含主键列(同等于RowCount办法中的排序列),如许能够加速义务完成速率。只要在哀求页中,才取得一切必要的字段。而且,在终极查询中不存在字段别号,在跳行查询中,必需提早利用索引列。
行计数(RowCount)存储历程有一个别的的成绩,要完成通用化,在ORDERBY语句中只同意有一个列,这也是升序-降序办法和游标办法的成绩,固然他们能够对几个列举行排序,可是必需包管主键中只要一个字段。我猜假如用更多的静态SQL是能够办理这个成绩的,可是在我看来这不是很值得。固然如许的情形很有大概产生,但他们产生的频次不是很高。一般你能够用下面的道理也自力的分页存储历程。
功能测试
在测试中,我利用了四种办法,假如你有更好的办法的话,我很有乐趣晓得。不论怎样,我必要对这些办法举行对照,而且评价它们的功能。起首我的第一个设法就是写一个asp.net包括分页DataGrid的测试使用程序,然后测试页面了局。固然,这没法反应存储历程的实在呼应工夫,以是把持台使用程序显得加倍合适。我还到场了一个Web使用程序,但不是为了功能测试,而是一个关于DataGrid自界说分页和存储历程一同事情的例子。
在测试中,我利用了一个主动天生得年夜数据表,也许拔出了500000条数据。假如你没有一张如许的表来做实行,你能够点击这里下载一段用于天生数据的表计划和存储历程剧本。我没有利用一个自增的主键列,而是用一个独一辨认码来辨认纪录的。假如我利用下面提到的剧本,你大概会思索在天生表以后增加一个自增列,这些自增数据会依据主键举行数字排序,这也意味着你盘算用一个带有主键排序的分页存储历程来取得以后页的数据。
为了完成功能测试,我是经由过程一个轮回屡次挪用一个特定的存储历程,然后盘算均匀响应工夫来完成的。思索到缓存的缘故原由,为了更正确地建模实践情形――统一页面临于一个存储历程的屡次挪用取得数据的工夫一般是不合适用来做评价的,因而,我们在挪用统一个存储历程时,每次挪用所哀求的页码应当是随机的。固然,我们必需假定页的数目是流动的,10-20页,分歧页码的数据大概被猎取良多次,可是是随机猎取的。
有一点我们很简单注重到,响应工夫是由要猎取的页数据相对了局集入手下手的地位的间隔决意的,越是阔别了局集的入手下手地位,就有越多的纪录要跳过,这也是我为何不把前20也包含进我的随机序列的缘故原由。作为交换,我会利用2的n次方个页面,轮回的巨细是必要的分歧页的数目*1000,以是,每一个页面几近都被猎取了1000次(因为随机缘故原由,一定会有所偏向)
了局
这里有我的测试了局:
结论
测试是依照从功能最好到最差的按次举行的――行计数、游标、升序-降序、子查询。有一件事很风趣,一般人们很少会会见前五页以后的页面,因而子查询办法大概在这类情形下满意你的必要,这得看你的了局集的巨细和关于远间隔(distant)页面的产生频次展望,你也很有大概利用这些办法的组合形式。假如是我,在任何情形下,我都更喜好用行计数办法,它运转起来非常不错,即便关于第一页也是云云,这里的“任何情形”代表了一些很难完成通用化的情形,在这类情形下,我会利用游标。(关于前两种我大概利用子查询办法,以后再用游标办法)因为各系统的API不同,代码调用API编写程序就会遇到很多不兼容的地方,比如Java改写后的Serv-U就不能在手机上执行,手机的游戏也不能直接在微机上执行。 |
|