Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
JBelthoff
Posting Yak Master
173 Posts |
Posted - 2005-06-09 : 13:52:31
|
For the fastest paging solution, one must use a cursor! I know what your thinking... Hogwash! Right?Well, I would not have believed it myself but I tried it and it indeed works. First I would like to give credit where it is due by saying that this solutions comes from NOT me but rather from Justin Lovell. His fast paging solution is nothing short of brilliant -- IMHO of course. Justin's solution can be found on his blog located here: Justin Lovell Enterprise PagingHere is the data I used for my experiment so you may try it for yourself. Kudos Justin!--## FIRST Create the Very Large Table if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[VeryLargeTable]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)drop table [dbo].[VeryLargeTable]GOCREATE TABLE [dbo].[VeryLargeTable] ( [VLTID] [int] IDENTITY (1, 1) NOT NULL , [FirstName] [varchar] (25) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [LastName] [varchar] (25) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [OtherData] [varchar] (25) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ) ON [PRIMARY]GO--## SECOND Load some data--## As we are adding 1 million records to the database..--## please be patient as it could take some time.--## If you already have a very large table to work from --## you can use use that instead.DECLARE @x int SET @x = 1WHILE @x < 1000000 BEGIN INSERT VeryLargeTable ( FirstName, LastName, OtherData ) VALUES ( 'John' + ' ' + CAST ( @x AS varchar(100) ), 'Doe' + ' ' + CAST ( @x AS varchar(100) ), 'YAK Stuff' + ' ' + CAST ( @x AS varchar(100) ) ) SET @x = @x + 1 ENDGO--## THRIRD Check our dataSELECT COUNT(*) FROM VeryLargeTableSELECT TOP 20 * FROM VeryLargeTableWHERE VLTID > 375482GO--## FOURTH Create our old Slow procedure using a temp var tableCREATE PROCEDURE dbo.TestVeryLargePaging_Slow @Page int, @RecsPerPage int, @TotalPages int OUTPUTASSELECT @TotalPages = ( SELECT COUNT(*)/@RecsPerPage + 1 FROM VeryLargeTable ) DECLARE @TableVar table ( TID int identity(1,1) PRIMARY KEY, VLTID int NOT NULL, FirstName varchar(25) NOT NULL, LastName varchar(25) NOT NULL, OtherData varchar(25) NOT NULL ) INSERT INTO @TableVar (VLTID, FirstName, LastName, OtherData) SELECT VLTID, FirstName, LastName, OtherData FROM VeryLargeTable ORDER BY VLTIDDECLARE @FirstRec int, @LastRec intSELECT @FirstRec = (@Page - 1) * @RecsPerPageSELECT @LastRec = (@Page * @RecsPerPage + 1)SELECT TID, VLTID, FirstName, LastName, OtherDataFROM @TableVarWHERE TID > @FirstRec AND TID < @LastRecGO--## FIFTH Create our Fast Cursor ProcedureCREATE PROCEDURE dbo.TestVeryLargePaging_Fast @Page int, @RecsPerPage int, @TotalPages int OUTPUTASDECLARE @FirstRec intSELECT @FirstRec = (@Page -1) * @RecsPerPage + 1DECLARE @RowCount intSELECT @RowCount = @RecsPerPageSELECT @TotalPages = ( SELECT COUNT(*)/@RecsPerPage + 1 FROM VeryLargeTable )DECLARE @PK intDECLARE @tmpTable TABLE ( PK int NOT NULL PRIMARY KEY)DECLARE PagingCursor CURSOR DYNAMIC READ_ONLY FORSELECT VLTID FROM VeryLargeTableORDER BY VLTIDOPEN PagingCursor FETCH RELATIVE @FirstRec FROM PagingCursor INTO @PK WHILE (@RowCount <> 0) And (@@Fetch_Status = 0) BEGIN INSERT INTO @tmpTable (PK) VALUES (@PK) FETCH NEXT FROM PagingCursor INTO @PK SET @RowCount = @RowCount - 1 END CLOSE PagingCursorDEALLOCATE PagingCursor SELECT VLTID, FirstName, LastName, OtherData FROM VeryLargeTable lt JOIN @tmpTable temp ON lt.VLTID = temp.PKGO--## LAST Execute the two queries!--## Open up two tiled windows and run these side by side!--## 1DECLARE @Total intEXEC dbo.TestVeryLargePaging_Slow 6920, 50, @Total OUTPUTPRINT @Total--## 2DECLARE @Total intEXEC dbo.TestVeryLargePaging_Fast 6920, 50, @Total OUTPUTPRINT @TotalGO--## To Clean up the messDROP TABLE dbo.VeryLargeTableDROP PROCEDURE dbo.TestVeryLargePaging_FastDROP PROCEDURE dbo.TestVeryLargePaging_SlowGO Enjoy everyone and Thank you Justin Lovell!JB |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2005-06-09 : 14:36:12
|
while this may be true... i don't think this qualifies this as the fastest method.. for example, you are testing against a crappy non-cursor paging method. In order to compare, you would need to work out the most efficient non-cursor paging method to compare against.I believe it is something along the lines of:Declare @Page int, @RecsPerPage int, @execStr varchar(2000)Set @Page = 6920Set @RecsPerPage = 50Declare @tempTable table (pk int)Set @execStr = 'Select top ' + convert(varchar,@RecsPerPage) + ' pk from (Select top ' + convert(varchar,@RecsPerPage*(@page-1)+1) + ' pk from veryLargeTable Order By pk) A order by pk desc' Insert into @tempTableexec(@execStr)Select * From veryLargeTable AInner Join @execStr BOn A.pk = B.pk CoreySecret Service Agent: Mr. President, you're urinating on me.President Lyndon Johnson: I know I am. It's my prerogative. |
|
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2005-06-09 : 14:43:20
|
What exactly is "Enterprise" paging anyway? How does it differ from non-enterprise paging? Do I need the Enterprise Edition of SQL Server to use this cursor? (man, THAT would be a great feature, cursors are only available in Enterprise Edition) I guess they don't use indexes in "enterprise" databases either... |
|
|
JBelthoff
Posting Yak Master
173 Posts |
Posted - 2005-06-09 : 15:36:02
|
Hi Seventhnight,I see what you are doing. You replace the cursor with your query.However, when I run your code I get the following errors.Server: Msg 197, Level 15, State 1, Line 15EXECUTE cannot be used as a source when inserting into a table variable.Server: Msg 137, Level 15, State 1, Line 19Must declare the variable '@execStr'. So I changed it to this an ran some more tests.CREATE PROCEDURE dbo.TestVeryLargePaging_Fast_New @Page int, @RecsPerPage int ascreate table #tempTable(pk int)Declare @execStr varchar(2000)Set @execStr = 'Select top ' + convert(varchar,@RecsPerPage) + ' VLTID from (Select top ' + convert(varchar,@RecsPerPage*(@page-1)+1) + ' VLTID from veryLargeTable Order By VLTID) A order by VLTID desc' Insert into #tempTableexec(@execStr)Select * From veryLargeTable AJoin #tempTable BOn A.VLTID = B.pkdrop table #tempTablego The results show that on lower pages, 1-5000 your solution is quite a bit faster, but when the page gets higher than about 5000 yours is slightly slower than what I popsted. At least that is what I got.Thanks for the help, I think I'm going for your solution.Do you know of a way to use the Table var instead of the TempTable in this instance?Thanks,JB |
|
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2005-06-09 : 16:10:59
|
Now that I thnik about it... I don't think you can use the table variable. However, you probably could cut out the last step by changing the execstr slightly...CREATE PROCEDURE dbo.TestVeryLargePaging_Fast_New @Page int, @RecsPerPage int asDeclare @execStr varchar(2000)Set @execStr = 'Select Z.* From veryLargeTable Z Inner Join (Select top ' + convert(varchar,@RecsPerPage) + ' VLTID from (Select top ' + convert(varchar,@RecsPerPage*(@page-1)+1) + ' VLTID from veryLargeTable Order By VLTID) A order by VLTID desc) Y On Z.pk = Y.pk order By Z.VLTID' exec(@execStr)go CoreySecret Service Agent: Mr. President, you're urinating on me.President Lyndon Johnson: I know I am. It's my prerogative. |
|
|
JBelthoff
Posting Yak Master
173 Posts |
Posted - 2005-06-09 : 16:36:42
|
Hi Corey,I minor change and now this is screamin'...!You had + convert(varchar,@RecsPerPage*(@page-1)+1) + I changed to + convert(varchar,@RecsPerPage*(@page)) + Like thisALTER PROCEDURE dbo.TestVeryLargePaging_Fast_New @Page int, @RecsPerPage int, @TotalPages int OUTPUTasSELECT @TotalPages = ( SELECT COUNT(*)/@RecsPerPage + 1 FROM VeryLargeTable )Declare @execStr varchar(2000)Set @execStr = 'Select Z.* From veryLargeTable Z Inner Join (Select top ' + convert(varchar,@RecsPerPage) + ' VLTID from (Select top ' + convert(varchar,@RecsPerPage*(@page)) + ' VLTID from veryLargeTable Order By VLTID) A order by VLTID desc) Y On Z.VLTID = Y.VLTID order By Z.VLTID' exec(@execStr)go Thank you so much. I have been looking for a way to make large table paging do-able without dumping everything over to a temp.Justin was brilliant, but you are a genious!It must be hot down there in SC today huh? It's hot here in CT! Whew!JB |
|
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2005-06-09 : 19:50:07
|
its muggy down here in SC... a little bit of rain to cool us off though ... too bad I can't take all the credit for the solution... I'm sure I've seen it here a few times CoreySecret Service Agent: Mr. President, you're urinating on me.President Lyndon Johnson: I know I am. It's my prerogative. |
|
|
raclede
Posting Yak Master
180 Posts |
Posted - 2005-06-09 : 20:50:39
|
well... paging comparison you can take a look at this onehttp://www.aspfaq.com/show.asp?id=2120"If the automobile had followed the same development cycle as the computer, a Rolls-Royce would today cost $100, get a million miles per gallon, and explode once a year, killing everyone inside. "raclede |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
|
DonAtWork
Master Smack Fu Yak Hacker
2167 Posts |
Posted - 2005-06-10 : 10:03:08
|
SO to sum up, Cursor = poo *need more coffee*SELECT * FROM Users WHERE CLUE > 0(0 row(s) affected) |
|
|
hypa
Starting Member
1 Post |
Posted - 2005-07-01 : 14:34:19
|
quote: Originally posted by JBelthoff Hi Corey,I minor change and now this is screamin'...!You had + convert(varchar,@RecsPerPage*(@page-1)+1) + I changed to + convert(varchar,@RecsPerPage*(@page)) + Like thisALTER PROCEDURE dbo.TestVeryLargePaging_Fast_New @Page int, @RecsPerPage int, @TotalPages int OUTPUTasSELECT @TotalPages = ( SELECT COUNT(*)/@RecsPerPage + 1 FROM VeryLargeTable )Declare @execStr varchar(2000)Set @execStr = 'Select Z.* From veryLargeTable Z Inner Join (Select top ' + convert(varchar,@RecsPerPage) + ' VLTID from (Select top ' + convert(varchar,@RecsPerPage*(@page)) + ' VLTID from veryLargeTable Order By VLTID) A order by VLTID desc) Y On Z.VLTID = Y.VLTID order By Z.VLTID' exec(@execStr)go Thank you so much. I have been looking for a way to make large table paging do-able without dumping everything over to a temp.Justin was brilliant, but you are a genious!It must be hot down there in SC today huh? It's hot here in CT! Whew!JB
This method does not appear to retrieve accurate results after page 2, have you tested this? |
|
|
|
|
|
|
|