Vous êtes sur la page 1sur 10

Introduction

Developers and database administrators have long debated methods for paging recordset results from Microsoft SQL Server, trying to balance ease of use with performance. The simplest methods were less efficient because they retrieved entire datasets from SQL Server before eliminating records which were not to be included, while the best-performing methods handled all paging on the server with more comple scripting. The !"#$%&M'(!)* function introduced in SQL Server +,,- provides an efficient way to limit results relatively easily.
Paging Efficiency

.n order to scale well, most applications only wor/ with a portion of the available data at a given time. #eb-based data maintenance applications are the most common e ample of this, and several databindable 0S1.%(T classes )such as 2rid3iew and Datagrid* have built-in support for paging results. #hile it is possible to handle paging within the web page code, this may re4uire transferring all of the data from the database server to the web server every time the control is updated. To improve performance and efficiency, data which will not be used should be eliminated from processing as early as possible.
Paging Methods

Many popular databases offer functions allowing you to limit which rows are returned for a given 4uery based upon their position within the record set. 5or e ample, MySQL provides the L.M.T 4ualifier, which ta/es two parameters. The first L.M.T parameter specifies which )6ero-based* row number will be the first record returned, and the second parameter specifies the ma imum number of records returned. The 4uery7
SELECT * FROM table LIMIT 20,13

...will return the +,th through the 8+nd records -- assuming at least 88 records are available to return. .f fewer than 88 records are available, the 4uery will return all records from record +, on. .f fewer than +, records are available, none will be returned. SQL Server does not have this functionality, however the +,,release does have a number of other new tric/s. 5or instance, support for 9L! procedures means it is possible to use e isting paging methods to write 3'.%(T or 9: code that would e ecute within the SQL Server environment. &nfortunately, 9L! procedures

are not as efficient as native Transact SQL. To ensure best performance, 4ueries should still be written in TSQL whenever practical.
Using ROW_NUMBER()

TSQL in the +,,- release includes the !"#$%&M'(!)* function, which adds an integer field to each record with the record;s ordinal result set number. Stated more simply, it adds the record;s position within the result set as an additional field so that the first record has a <, the second a +, etc. This may appear to be of little value, however by using nested 4ueries we can use this to our advantage. To demonstrate !"#$%&M'(!)* and to e plore how the paging solution wor/s, create a simple salary table and populate it with random data using the following commands7
CREATE TABLE [dbo].[Salarie ]! ["er o#] [#$ar%&ar]!'0( )OT )*LL, [i#%o+e] [+o#e,] )OT )*LL, CO)STRAI)T [-./ alarie ] -RIMAR0 .E0 CL*STERE1! ["er o#] ASC (( O) [-RIMAR0] 2O I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)SERT I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO I)TO Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie Salarie 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES 3AL*ES !45oe4, 4260004( !4S7e4, 4890004( !4Mi%&ael4, 4:'0004( !45o&#4, 49;0004( !4Ral"&4, 4160004( !4.are#4, 4;30004( !4<aldo4, 4:;0004( !4E$a4, 4'10004( !4E+er o#4, 46:0004( !4Sta#le,4, 4'80004( !45or=e4, 4:60004( !4Co# ta#%e4, 4'10004( !4A+elia4, 4390004( !4A##a4, 4:80004( !41a#ielle4, 4960004( !4Ste"&a#ie4, 4:;0004( !4Eli>abet&4, 4230004(

The !"#$%&M'(!)* function has no parameters - it simply adds the row number to each record in the result set. To ensure the numbering is consistent, however, SQL Server needs to /now how to sort the data. 'ecause of this, !"#$%&M'(!)* must immediately be followed by the "3(!)* function. "3(!)* has one re4uired parameter, which is an "!D(! '= clause. The basic synta for 4uerying the Salaries table is7

SELECT RO</)*MBER!( O3ER!OR1ER B0 "er o#(, "er o#, i#%o+e FROM Salarie

This returns the following result7


(No column name) person 1 2 3 4 5 6 # 8 9 10 11 12 13 14 15 16 1# Amelia Anna Danielle income 36000.00 49000.00 68000.00

Constance 51000.00 Eli a!et" 23000.00 Emerson 84000.00 E$a %oe %o"n %or&e 'aren (ic"ael )alp" *tanle+ *ue ,al-o 51000.00 28000.00 6#000.00 48000.00 #3000.00 45000.00 18000.00 59000.00 96000.00 4#000.00

*tep"anie 4#000.00

The Salaries data now appears sorted by person, and it has an e tra column indicating each record;s position within the results. .f for any reason you wanted the results to display in a different order than they were numbered in, you can include a different "!D(! '= clause as part of the normal S(L(9T synta 7
SELECT RO</)*MBER!( O3ER!OR1ER B0 "er o#(, "er o#, i#%o+e FROM Salarie OR1ER B0 i#%o+e

This returns the following result7


(No column name) person 13 5 8 1 )alp" %oe Amelia income 18000.00 28000.00 36000.00

Eli a!et" 23000.00

12 15 1# 10 2 3 # 14 9 4 11 6 16

(ic"ael ,al-o %or&e Anna E$a *tanle+ %o"n Danielle 'aren *ue

45000.00 4#000.00 48000.00 49000.00 51000.00 59000.00 6#000.00 68000.00 #3000.00 96000.00

*tep"anie 4#000.00

Constance 51000.00

Emerson 84000.00

.f we want to limit the results displayed to a certain range, we need to nest this S(L(9T inside another one and provide a name for the !"#$%&M'(!)* column. To limit our results to records - through >, we can use the following 4uery7
SELECT * FROM !SELECT RO</)*MBER!( O3ER!OR1ER B0 "er o#( AS ro?#7+, "er o#, i#%o+e FROM Salarie ( AS Salarie 1 <@ERE ro?#7+ AB ' A)1 ro?#7+ CB 8

This returns the following result7


ro.num person 5 6 # 8 9 income

Eli a!et" 23000.00 Emerson 84000.00 E$a %oe %o"n 51000.00 28000.00 6#000.00

0gain, we can change the sort order by adding an "!D(! '= clause. This is most easily accomplished by using the outer S(L(9T statement7
SELECT * FROM !SELECT RO</)*MBER!( O3ER!OR1ER B0 "er o#( AS ro?#7+, "er o#, i#%o+e FROM Salarie ( AS Salarie 1 <@ERE ro?#7+ AB ' A)1 ro?#7+ CB 8 OR1ER B0 i#%o+e

This returns the following result7


ro.num person 5 8 # 9 6 %oe E$a %o"n income 28000.00 51000.00 6#000.00

Eli a!et" 23000.00

Emerson 84000.00

.f we want to support the same type of arguments that MySQL;s L.M.T)* supports, we can create a stored procedure that accepts a beginning point and a ma imum number of records to return. !"#$%&M'(! re4uires that the data be sorted, so we will also have a re4uired parameter for the "!D(! '= clause. ( ecute the following statement to create a new stored procedure7
CREATE -ROCE1*RE [dbo].["a=eSalarie ] D tart i#t B 1 ,D+aE%t i#t B ' ,D ort #$ar%&ar!200( AS SET )OCO*)T O) 1ECLARE DSTMT #$ar%&ar!+aE(, FF SGL tate+e#t to eEe%7te D7bo7#d i#t IF D tart C IF D+aE%t C SET D7bo7#d SET DSTMT B 4( AS ro?, * 1 1 B 4 SET D tart B 1 SET D+aE%t B 1 D tart H D+aE%t SELECT "er o#, i#%o+e FROM ! SELECT RO</)*MBER!( O3ER!OR1ER B0 4 H D ort H FROM Salarie ( AS tbl <@ERE ro? AB 4 H CO)3ERT!$ar%&ar!8(, D tart( H 4 A)1 ro? C 4 H CO)3ERT!$ar%&ar!8(, D7bo7#d( FF ret7r# reJ7e ted re%ord

EIEC !DSTMT(

The pageSalaries procedure begins with S(T %"9"&%T "% to disable the record count message )a common step for optimi6ing 4uery performance*. #e then declare two necessary variables, ?STMT and ?ubound. 'ecause we want to be able to change what "!D(! '= argument is used, we need to dynamically generate our 4uery statement by storing it in ?STMT. The ne t lines ensure that only positive numbers are used for the starting position and ma imum si6e, then calculate the range of !"#$%&M'(!)* values being re4uested. ).f we wanted to be 6ero-based li/e MySQL;s L.M.T, we could do so with a few minor twea/s.* "nce the dynamic SQL

command has been strung together, it is e ecuted so that the results are returned. ( ecute the following statement to test the stored procedure7
"a=eSalarie :, ;, 4i#%o+e4

This returns the following result7


person Amelia (ic"ael ,al-o %or&e Anna income 36000.00 45000.00 4#000.00 48000.00 49000.00

*tep"anie 4#000.00

Constance 51000.00

.f we e ecute7
"a=eSalarie 13, ;, 4i#%o+e4

we receive bac/7
person %o"n 'aren *ue income 6#000.00 #3000.00 96000.00

Danielle 68000.00 Emerson 84000.00

... because the 4uery goes beyond the number of records available. Ta/ing this one step further, we can ma/e a stored procedure that does a more general form of paging. .n fact, it can be generali6ed to the point that it can be used to return any collection of fields, in any order, with any filtering clause. To create this wunder/ind marvel, e ecute the following command7
CREATE -ROCE1*RE [dbo].[7til-A2E] Ddata r% #$ar%&ar!200( ,DorderB, #$ar%&ar!200( ,DKieldli t #$ar%&ar!200( B 4*4 ,DKilter #$ar%&ar!200( B 44 ,D"a=e)7+ i#t B 1

,D"a=eSi>e i#t B )*LL AS SET )OCO*)T O) 1ECLARE DSTMT #$ar%&ar!+aE( ,Dre%%t i#t "a=i#= i#terKa%e(

FF SGL to eEe%7te FF total L oK re%ord

!Kor 2rid3ie?

IF LTRIM!RTRIM!DKilter(( B 44 SET DKilter B 41 B 14 IF D"a=eSi>e IS )*LL BE2I) SET DSTMT B 4SELECT 4 H DKieldli t H 4FROM 4 H Ddata r% H 4<@ERE 4 H DKilter H 4OR1ER B0 4 H DorderB, EIEC !DSTMT( FF ret7r# reJ7e ted re%ord E)1 ELSE BE2I) SET DSTMT B 4SELECT Dre%%t B CO*)T!*( FROM 4 H Ddata r% H 4 <@ERE 4 H DKilter EIEC "/eEe%7teSGL DSTMT, D"ara+ B )4Dre%%t I)T O*T-*T4, Dre%%t B Dre%%t O*T-*T SELECT Dre%%t AS re%%t FF ret7r# t&e total L oK re%ord 1ECLARE Dlbo7#d i#t, D7bo7#d i#t SET D"a=e)7+ B ABS!D"a=e)7+( SET D"a=eSi>e B ABS!D"a=eSi>e( IF D"a=e)7+ C 1 SET D"a=e)7+ B 1 IF D"a=eSi>e C 1 SET D"a=eSi>e B 1 SET Dlbo7#d B !!D"a=e)7+ F 1( * D"a=eSi>e( SET D7bo7#d B Dlbo7#d H D"a=eSi>e H 1 IF Dlbo7#d AB Dre%%t BE2I) SET D7bo7#d B Dre%%t H 1 SET Dlbo7#d B D7bo7#d F !D"a=eSi>e H 1( FF ret7r# t&e la t "a=e oK re%ord iK FF #o re%ord ?o7ld be o# t&e FF "e%iKied "a=e E)1 SET DSTMT B 4SELECT 4 H DKieldli t H 4 FROM ! SELECT RO</)*MBER!( O3ER!OR1ER B0 4 H DorderB, H 4( AS ro?, * FROM 4 H Ddata r% H 4 <@ERE 4 H DKilter H 4 ( AS tbl <@ERE ro? A 4 H CO)3ERT!$ar%&ar!8(, Dlbo7#d( H 4 A)1 ro? C 4 H CO)3ERT!$ar%&ar!8(, D7bo7#d( EIEC !DSTMT( FF ret7r# reJ7e ted re%ord E)1

=ou may receive the following error message from SQL Server, which you can confidently ignore7
Ca##ot add ro? to , . Jl/de"e#de#%ie Kor t&e tored "ro%ed7re be%a7 e it de"e#d o# t&e +i i#= table 4 "/eEe%7teSGL4. T&e tored

"ro%ed7re ?ill till be %reatedM &o?e$er, it %a##ot be eEe%7ted 7#til t&e table eEi t .

7%%e

K7ll,

The util1age procedure accepts @ parameters7


/-atasrc /or-er2+ /5iel-lis /5ilter /pa&eNum /pa&e*i e 0 t"e ta!le (or store- proce-ure1 etc.) name 0 t"e 3)DE) 24 clause 0 t"e 5iel-s to return (inclu-in& calculate- e6pressions) 0 t"e ,7E)E clause 0 t"e pa&e to return (must !e &reater t"an or e8ual to one) 0 t"e num!er o5 recor-s per pa&e

The stored procedure needs the name of a data source to 4uery against )such as a table* and one or more fields to sort by )since "3(!)* re4uires an "!D(! '= clause*. .f ?filter is blan/ )the default*, it will be set to A< B <A as a simple way to select all records. .f ?pageSi6e is not supplied, the 4uery will run without paging and will not return a record count. .f, however, ?pageSi6e is supplied, a version of the 4uery is e ecuted to get the total number of records. .n order to have this record count available within the procedure and as a returned value, we use sp$e ecuteSQL to support e ecuting the statement while returning an output parameter. The record count is used to prevent returning empty results when possible, and to support paging interfaces that calculate the number of pages available )such as 2rid3iew*. .f we were calling this stored procedure to populate a 2rid3iew, we would return ?recct as a !eturn3alue parameter instead of using a result set, but we will use a result set for demonstration purposes. The procedure calculates what the actual record positions will be for the re4uested page. !ather than allow the 4uery to fail, there are safety chec/s ensuring that ?pageSi6e and ?page%um are greater than 6ero, and that the result set will not be empty. .f the specified page is out of range, this procedure will return the last possible page of records. This is helpful if a user changes more than one setting before refreshing their data, or if a significant amount of data is deleted between re4uests. The remainder of the procedure is virtually identical to the pageSalaries procedure. To test the util102( stored procedure, e ecute the following statement7
7til-A2E 4Salarie 4, 4"er o#4, 4*4, 4i#%o+e A 10004, 2, :

This returns the following two result sets7

recct 1# ro. person 5 6 # 8 income

Eli a!et" 23000 Emerson 84000 E$a %oe 51000 28000

.f we e ecute7
7til-A2E 4Salarie 4, 4"er o#4, 4"er o#, i#%o+e4, 44, 13, 3

...we receive bac/7


recct 1# person *ue ,al-o income 96000 4#000

*tep"anie 4#000

(ven though the re4uest should be for records 8@ through 8C - far outside of what is available - the procedure returns the last available page of records. .n contrast, re4uesting the third page with seven records per page using7
7til-A2E 4Salarie 4, 4"er o#4, 4"er o#, i#%o+e4, 44, 3, ;

...returns the last three records, as the page is not completely out of bounds7
person *ue ,al-o income 96000 4#000

*tep"anie 4#000

0ll of these e amples are based on simple single-table 4ueries, which may not reflect what you need in the real world. #hile the util102( procedure does not support ad-hoc D".%s, it does wor/ with SQL 3iews. .f you want paging support for multi-table 4ueries,

you should create a 3iew )with all of the necessary D".%s* to use as the data source. &sing a 3iew follows good design practices as it ensures that your Doins are performed consistently, allows easier adhoc 4uerying from the command line, and is much easier to troubleshoot than a stored procedure;s dynamic S(L(9T statement logic.
Conclusion

#hile SQL Server does not have as simple a method for paging results as some other databases, features introduced in the +,,release have made it possible to page results efficiently more easily than ever before. .n the ne t article in this series, we will go a step further and integrate this paging logic with a 2rid3iew through a Data 0ccess Layer.

Vous aimerez peut-être aussi