Add ordinal column to stored procedure - sql

I'd like to add a column to a stored procedure that simply counts up from 1, irrespective of how the data is sorted or which page I'm fetching (using offset.)
The stored procedure itself looks like this:
SELECT
e.EntityId, e.HierarchyId, e.ParentNode, e.NgageId, e.NgageParentId,
e.Type, e.IsDeleted, e.LastIndexedDate, e.CurrentVersion, e.Level,
v.EntityVersionID, v.VerisonNotes, v.Title, v.[Description], v.FormID,
v.IsPublic, v.CreatedDate, v.LastModifiedDate, v.ExpirationDate,
d.DocumentID, d.OwnerId AS OwnerId,
o.ADUsername AS OwnerAccount, o.Name AS Author,
d.[Status], d.DocumentType, d.CheckoutUserId AS CheckoutUserId,
c.ADUsername AS CheckoutUserAccount, c.Name AS CheckoutUserName,
d.CheckoutDate,
dbo.GetCommaSeperatedTags(e.EntityID) AS Tags
--INTO #documentsTemp
FROM
dbo.Entities AS e
INNER JOIN
dbo.EntityVersions AS v ON v.EntityID = e.EntityID AND e.CurrentVersion = v.VersionNumber
INNER JOIN
dbo.Documents AS d ON d.EntityVersionID = v.EntityVersionID
INNER JOIN
dbo.Users AS o ON o.UserID = d.OwnerId
LEFT JOIN
dbo.Users AS c ON c.UserID = d.CheckoutUserId
WHERE
[Type] = 5
AND NgageID = #ngageId
AND IsDeleted = 0
AND dbo.CheckPermissions(e.EntityId, #userId, 'LIST') = 1
ORDER BY
CASE WHEN #orderBy = 'Title' THEN v.Title END,
CASE WHEN #orderBy = 'Title' AND #desc = 1 THEN v.Title END DESC,
CASE WHEN #orderBy = 'Author' THEN o.Name END,
CASE WHEN #orderBy = 'Author' AND #desc = 1 THEN o.Name END DESC,
CASE WHEN #orderBy = 'DateModified' THEN v.LastModifiedDate END,
CASE WHEN #orderBy = 'DateModified' AND #desc = 1 THEN v.LastModifiedDate END DESC
OFFSET ((#pageNumber - 1) * #pageSize) ROWS
FETCH NEXT #pageSize ROWS ONLY;
What is the best way to do this?
Thanks,
Joe

I see three options (two of them are already mentioned by Gordon and the third one is not recommended):
do this at the application level
repeat the ORDER BY clause and do some arithmetic
rely on undocumented behaviour, with potentially incorrect results
Here are options 2 and 3 in AdventureWorks (to shorten the queries):
DECLARE #pageSize int=5, #pagenumber int=2, #OrderBy varchar(20)='Name'
SELECT (ROW_NUMBER() OVER (
ORDER BY
CASE WHEN #OrderBy='Name' THEN Name END,
CASE WHEN #OrderBy='ProductNumber' THEN ProductNumber END
)-1)%#pageSize+1 as N, *
FROM Production.Product
ORDER BY
CASE WHEN #OrderBy='Name' THEN Name END,
CASE WHEN #OrderBy='ProductNumber' THEN ProductNumber END
OFFSET ((#pageNumber - 1) * #pageSize) ROWS
FETCH NEXT #pageSize ROWS ONLY
SELECT ROW_NUMBER() OVER (ORDER BY #pageSize) AS RN, *
FROM (
SELECT * FROM Production.Product
ORDER BY
CASE WHEN #OrderBy='Name' THEN Name END,
CASE WHEN #OrderBy='ProductNumber' THEN ProductNumber END
OFFSET ((#pageNumber - 1) * #pageSize) ROWS
FETCH NEXT #pageSize ROWS ONLY
) X
Razvan

Related

Select unique rows from a select query result

I have procedure in which I am getting data from 4 tables by joining them.
Here is my code
alter PROCEDURE [dbo].[GetServiceRequestforLead]
#professionalArea nvarchar (35) = '',
#status nvarchar (15) = '',
#experience nvarchar (15) = '',
#PageNumber BIGINT = 1,
#PageSize BIGINT =20,
#userid bigint = 0
AS
BEGIN
SELECT *
FROM (
SELECT Row_number() OVER (ORDER BY sc.Addeddate DESC) AS Row, sc.*
, TotalRows = Count(*) OVER()
FROM (
select Mod_UserDetails.Email as LeadEmailId, Mod_UserDetails.ID as LeaduserId
, ProfessionType, Mod_UserDetails.FirstName as LeadFirstName, Mod_UserDetails.LastName as LeadLastName
, Mod_UserDetails.PhoneNo1 as LeadMobile
, f.*
from Lead_RequestForm f
inner join Lead_ProfessionalArea on Lead_ProfessionalArea.ID = f.Profession
left join ServiceProviderRequestMapping on f.RequestId = ServiceProviderRequestMapping.RequestId
left join mod_userdetails on mod_userdetails.ID = ServiceProviderRequestMapping.Leaduserid
where f.ExperienceRequired like '%'+#experience+'%'
AND f.Status like '%' + #status + '%'
AND Lead_ProfessionalArea.ProfessionType like '%'+ #professionalArea +'%'
AND (ServiceProviderRequestMapping.IsDeleted is null OR ServiceProviderRequestMapping.IsDeleted = 0)
) sc
) AS query
WHERE row > ( #PageSize *( #PageNumber -1))
AND row <= ( #PageSize * #PageNumber )
END
By executing this I am getting more than one row for RequestId column of Lead_RequestForm f table. Data is unique in both the rows but reuqest id is the same. I want to get only the first record with the same Requestid column.
i have added snap here request id column has duplicate value i want only one roq with a request id so want to select first one only
You can do as below -
alter PROCEDURE [dbo].[GetServiceRequestforLead]
#professionalArea nvarchar (35) = '',
#status nvarchar (15) = '',
#experience nvarchar (15) = '',
#PageNumber BIGINT = 1,
#PageSize BIGINT =20,
#userid bigint = 0
AS
BEGIN
Select Tb.*
from
(SELECT t.*, row_number() OVER (partition by RequestId order by Rowid) as seq_nbr
FROM (
SELECT Row_number() OVER (ORDER BY sc.Addeddate DESC) AS Row, sc.*
, TotalRows = Count(*) OVER()
FROM (
select Mod_UserDetails.Email as LeadEmailId, Mod_UserDetails.ID as LeaduserId
, ProfessionType, Mod_UserDetails.FirstName as LeadFirstName, Mod_UserDetails.LastName as LeadLastName
, Mod_UserDetails.PhoneNo1 as LeadMobile
, f.*
from Lead_RequestForm f
inner join Lead_ProfessionalArea on Lead_ProfessionalArea.ID = f.Profession
left join ServiceProviderRequestMapping on f.RequestId = ServiceProviderRequestMapping.RequestId
left join mod_userdetails on mod_userdetails.ID = ServiceProviderRequestMapping.Leaduserid
where f.ExperienceRequired like '%'+#experience+'%'
AND f.Status like '%' + #status + '%'
AND Lead_ProfessionalArea.ProfessionType like '%'+ #professionalArea +'%'
AND (ServiceProviderRequestMapping.IsDeleted is null OR ServiceProviderRequestMapping.IsDeleted = 0)
) sc
) AS query
WHERE row > ( #PageSize *( #PageNumber -1))
AND row <= ( #PageSize * #PageNumber )
)t
)Tb
where Tb.seq_nbr = 1
END

ambigious column name in sql server

hello i'm having a ambiguous column name in m stored procedure for payment .bid-id can someone help to resolve this issue please?
SET NOCOUNT ON;
SELECT ROW_NUMBER() OVER
(
ORDER BY [PaymentID] ASC
)AS RowNumber
,[PaymentID]
,[Name]
,[WinningPrice]
,[PaymentDate]
,[Payment.BidID]
INTO #Results
FROM Item INNER JOIN
Auction ON Item.ItemID = Auction.ItemID INNER JOIN
BID ON Auction.AuctionID = BID.AuctionID INNER JOIN
Payment ON BID.BidID = Payment.BidID
Where (BID.Status = 'Paid') AND (BID.BuyerID = #buyer)
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
End
There is a column name you use in the query that is available in multiple tables.
Without the table structure we can't be certain which one it is, but probably one with an alias in your query:
,[PaymentID]
,[Name]
,[WinningPrice]
,[PaymentDate]
Try this, Create a alias for ambiguous column name
SET NOCOUNT ON;
SELECT ROW_NUMBER() OVER
(
ORDER BY [PaymentID] ASC
)AS RowNumber
,[PaymentID]
,[Name]
,[WinningPrice]
,[PaymentDate]
,[Payment.BidID] as PBidID
INTO #Results
FROM Item INNER JOIN
Auction ON Item.ItemID = Auction.ItemID INNER JOIN
BID ON Auction.AuctionID = BID.AuctionID INNER JOIN
Payment ON BID.BidID = Payment.BidID
Where (BID.Status = 'Paid') AND (BID.BuyerID = #buyer)
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
End
Good practice is using aliases like:
SET NOCOUNT ON;
SELECT ROW_NUMBER() OVER
(
ORDER BY i.[PaymentID] ASC --which table it belongs? put correct alias
)AS RowNumber
,i.[PaymentID]
,i.[Name]
,i.[WinningPrice]
,i.[PaymentDate]
,p.[BidID]
INTO #Results
FROM Item i
INNER JOIN Auction a
ON i.ItemID = a.ItemID
INNER JOIN BID b
ON a.AuctionID = b.AuctionID
INNER JOIN Payment p
ON b.BidID = p.BidID
Where (b.Status = 'Paid')
AND (b.BuyerID = #buyer)
SELECT #RecordCount = COUNT(*)
FROM #Results
SET NOCOUNT ON;
SELECT ROW_NUMBER() OVER
(
ORDER BY [PaymentID] ASC
)AS RowNumber
,[PaymentID]
,[Name]
,[WinningPrice]
,[PaymentDate]
,[Payment.BidID]
INTO #Results
FROM Item INNER JOIN
Auction ON Item.ItemID = Auction.ItemID INNER JOIN
BID ON Auction.AuctionID = BID.AuctionID INNER JOIN
**Payment P1 ON BID.BidID = P1.BidID**
Where (BID.Status = 'Paid') AND (BID.BuyerID = #buyer)
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
End
Hope this helps..
highlighted the line which i changed

SQL Server processing for jquery datatables order by issue?

I have written the following script to mimic the incoming parameters from datatables and try to filter the results using the parameters. Everything works fine except the order by clause. Basically it only orders by rownumber and does not take into consideration the case statement which provides the second order by column.
declare #sSortColumn as nvarchar(50)='Country';
declare #sSortDirection as nvarchar(5) = 'Desc';
declare #sSearch as nvarchar(50) = '';
declare #iDisplayLength as int = 20;
declare #iDisplayStart as int = 20;
declare #sIDsearch as int = CASE WHEN ISNUMERIC(#sSearch) = 1 THEN CAST(#sSearch AS INT) ELSE 0 END;
WITH media AS
(
select ROW_NUMBER() OVER (ORDER BY mc.id) as RowNumber,
mc.id,mc.Name, mc.CityID,lc.Name as Country
from Lookup_MediaChannels mc
left join Lookup_SonarMediaTypes st on mc.SonarMediaTypeID = st.ID
left join Lookup_SonarMediaGroups sg on sg.ID = st.SonarMediaGroupID
left join Lookup_MediaTypes mt on mc.MediaTypeID = mt.ID
left join Lookup_SonarMediaGroups sg1 on sg1.ID = mt.MediaGroupID
left join lookup_Countries lc on lc.id = mc.countryid
where mc.Name like '%'+#sSearch+'%'
and (sg1.ID=1 or sg.ID =1 )
or mc.id = #sIDsearch
)
SELECT RowNumber, Name, Country
FROM media
WHERE RowNumber BETWEEN (#iDisplayStart+ 1) AND (#iDisplayStart+ #iDisplayLength)
order by rownumber,
CASE WHEN #sSortColumn = 'Name' AND #sSortDirection = 'Desc'
THEN Name END DESC,
CASE WHEN #sSortColumn = 'Name' AND #sSortDirection != 'Desc'
THEN Name END,
CASE WHEN #sSortColumn = 'Country' AND #sSortDirection = 'Desc'
THEN Country END DESC,
CASE WHEN #sSortColumn = 'Country' AND #sSortDirection != 'Desc'
THEN Country END
This simplified example may help you
http://sqlfiddle.com/#!6/35ffb/10
Set up some dummy data (this would be replaced by your select statement)
create table x(
id int,
name varchar(3),
country varchar(2)
)
insert into x
values (1,'aaa','uk'),
(2,'aaa','us'),
(3,'baa','uk'),
(4,'caa','uk'),
(5,'baa','it')
Some vars to hold sort field and sort order
declare #so char(1)
declare #sf char(1)
set #so = 'a' -- a = asc d = desc
set #sf = 'n' -- n = name c = country
and a select to return sorted data
SELECT row_number()
OVER (order by
CASE WHEN #so = 'd' THEN sf END desc,
CASE WHEN #so <> 'd' THEN sf end,
id
) AS aa, name,country
FROM (
SELECT x.*, case #sf when 'n' then name when 'c' then country end sf
FROM X
) X

Stored Procedure and output parameter from paging script (SQL Server 2008)

I have the below stored procedure and would like to only have one SQL statement. At the moment you can see there are two statements, one for the actual paging and one for a count of the total records which needs to be return to my app for paging.
However, the below is inefficient as I am getting the total rows from the first query:
COUNT(*) OVER(PARTITION BY 1) as TotalRows
How can I set TotalRows as my output parameter?
ALTER PROCEDURE [dbo].[Nop_LoadAllOptimized]
(
#PageSize int = null,
#PageNumber int = null,
#WarehouseCombinationID int = null,
#CategoryId int = null,
#OrderBy int = null,
#TotalRecords int = null OUTPUT
)
AS
BEGIN
WITH Paging AS (
SELECT rn = (ROW_NUMBER() OVER (
ORDER BY
CASE WHEN #OrderBy = 0 AND #CategoryID IS NOT NULL AND #CategoryID > 0
THEN pcm.DisplayOrder END ASC,
CASE WHEN #OrderBy = 0
THEN p.[Name] END ASC,
CASE WHEN #OrderBy = 5
THEN p.[Name] END ASC,
CASE WHEN #OrderBy = 10
THEN wpv.Price END ASC,
CASE WHEN #OrderBy = 15
THEN wpv.Price END DESC,
CASE WHEN #OrderBy = 20
THEN wpv.Price END DESC,
CASE WHEN #OrderBy = 25
THEN wpv.UnitPrice END ASC
)),COUNT(*) OVER(PARTITION BY 1) as TotalRows, p.*, pcm.DisplayOrder, wpv.Price, wpv.UnitPrice FROM Nop_Product p
INNER JOIN Nop_Product_Category_Mapping pcm ON p.ProductID=pcm.ProductID
INNER JOIN Nop_ProductVariant pv ON p.ProductID = pv.ProductID
INNER JOIN Nop_ProductVariant_Warehouse_Mapping wpv ON pv.ProductVariantID = wpv.ProductVariantID
WHERE pcm.CategoryID = #CategoryId
AND (wpv.Published = 1 AND pv.Published = 1 AND p.Published = 1 AND p.Deleted = 0 AND pv.Deleted = 0 and wpv.Deleted = 0)
AND wpv.WarehouseID IN (select WarehouseID from Nop_WarehouseCombination where UserWarehouseCombinationID = #WarehouseCombinationID)
)
SELECT TOP (#PageSize) * FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
SELECT #TotalRecords = COUNT(p.ProductId) FROM Nop_Product p
INNER JOIN Nop_Product_Category_Mapping pcm ON p.ProductID=pcm.ProductID
INNER JOIN Nop_ProductVariant pv ON p.ProductID = pv.ProductID
INNER JOIN Nop_ProductVariant_Warehouse_Mapping wpv ON pv.ProductVariantID = wpv.ProductVariantID
WHERE pcm.CategoryID = #CategoryId
AND (wpv.Published = 1 AND pv.Published = 1 AND p.Published = 1 AND p.Deleted = 0 AND pv.Deleted = 0 and wpv.Deleted = 0)
AND wpv.WarehouseID IN (select WarehouseID from Nop_WarehouseCombination where UserWarehouseCombinationID = #WarehouseCombinationID)
END
I think I understand your issue here. Have you considered that the Count could be done BEFORE the CTE
and then passed in as value to the CTE as a variable.
i.e, set the value for #TotalRecords up front, pass it in, and so the CTE will use this count rather than executing the count a second time?
Does this make sense, or have I missed your point here.
no problem friend, highly possible i missed a trick here. However without the schema and data its tricky to test what I am suggesting. In the absence of someone giving a better answer, I've put this test script with data together to demo what I am talking about. If this isn't what you want then no problem. If it is just plain missing the point again, then I'll take that on the chin.
Declare #pagesize as int
Declare #PageNumber as int
Declare #TotalRowsOutputParm as int
SET #pagesize = 3
SET #PageNumber = 2;
--create some test data
DECLARE #SomeData table
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[SomeValue] [nchar](10) NULL
)
INSERT INTO #SomeData VALUES ('TEST1')
INSERT INTO #SomeData VALUES ('TEST2')
INSERT INTO #SomeData VALUES ('TEST3')
INSERT INTO #SomeData VALUES ('TEST4')
INSERT INTO #SomeData VALUES ('TEST5')
INSERT INTO #SomeData VALUES ('TEST6')
INSERT INTO #SomeData VALUES ('TEST7')
INSERT INTO #SomeData VALUES ('TEST8')
INSERT INTO #SomeData VALUES ('TEST9')
INSERT INTO #SomeData VALUES ('TEST10');
--Get total count of all rows
Set #TotalRowsOutputParm = (SELECT COUNT(SomeValue) FROM #SomeData p) ;
WITH Paging AS
(
SELECT rn = (ROW_NUMBER() OVER (ORDER BY SomeValue ASC)),
#TotalRowsOutputParm as TotalRows, p.*
FROM [SomeData] p
)
SELECT TOP (#PageSize) * FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
PRINT #TotalRowsOutputParm
I don't think you can do it without running the query twice if you want to assign it to a variable
however, can't you just add another column and do something like this instead?
;WITH Paging AS (select *,ROW_NUMBER() OVER(ORDER BY name) AS rn FROM sysobjects)
SELECT (SELECT MAX(rn) FROM Paging) AS TotalRecords,* FROM Paging
WHERE rn < 10
Or in your case
SELECT TOP (#PageSize) *,(SELECT MAX(PG.rn) FROM Paging) AS TotalRecords
FROM Paging PG
WHERE PG.rn > (#PageNumber * #PageSize) - #PageSize
Then from the front end grab that column
In the end I decided just to use two different SQL statements, one for count, one for select.
The "COUNT(*) OVER(PARTITION BY 1) as TotalRows" actually was pretty expensive and it turned out much quicker to just use two different statements.
Thank you everyone who helped with this question.

What is the best way to paginate results in SQL Server

What is the best way (performance wise) to paginate results in SQL Server 2000, 2005, 2008, 2012 if you also want to get the total number of results (before paginating)?
Finally, Microsoft SQL Server 2012 was released, I really like its simplicity for a pagination, you don't have to use complex queries like answered here.
For getting the next 10 rows just run this query:
SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
https://learn.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows-returned
Key points to consider when using it:
ORDER BY is mandatory to use OFFSET ... FETCH clause.
OFFSET clause is mandatory with FETCH. You cannot use ORDER BY ...
FETCH.
TOP cannot be combined with OFFSET and FETCH in the same query
expression.
Getting the total number of results and paginating are two different operations. For the sake of this example, let's assume that the query you're dealing with is
SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate
In this case, you would determine the total number of results using:
SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'
...which may seem inefficient, but is actually pretty performant, assuming all indexes etc. are properly set up.
Next, to get actual results back in a paged fashion, the following query would be most efficient:
SELECT *
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
FROM Orders
WHERE OrderDate >= '1980-01-01'
) AS RowConstrainedResult
WHERE RowNum >= 1
AND RowNum < 20
ORDER BY RowNum
This will return rows 1-19 of the original query. The cool thing here, especially for web apps, is that you don't have to keep any state, except the row numbers to be returned.
Incredibly, no other answer has mentioned the fastest way to do pagination in all SQL Server versions. Offsets can be terribly slow for large page numbers as is benchmarked here. There is an entirely different, much faster way to perform pagination in SQL. This is often called the "seek method" or "keyset pagination" as described in this blog post here.
SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < #previousScore)
OR (score = #previousScore AND player_id < #previousPlayerId)
ORDER BY score DESC, player_id DESC
The "seek predicate"
The #previousScore and #previousPlayerId values are the respective values of the last record from the previous page. This allows you to fetch the "next" page. If the ORDER BY direction is ASC, simply use > instead.
With the above method, you cannot immediately jump to page 4 without having first fetched the previous 40 records. But often, you do not want to jump that far anyway. Instead, you get a much faster query that might be able to fetch data in constant time, depending on your indexing. Plus, your pages remain "stable", no matter if the underlying data changes (e.g. on page 1, while you're on page 4).
This is the best way to implement pagination when lazy loading more data in web applications, for instance.
Note, the "seek method" is also called keyset pagination.
Total records before pagination
The COUNT(*) OVER() window function will help you count the number of total records "before pagination". If you're using SQL Server 2000, you will have to resort to two queries for the COUNT(*).
From SQL Server 2012, we can use OFFSET and FETCH NEXT Clause to achieve the pagination.
Try this, for SQL Server:
In the SQL Server 2012 a new feature was added in the ORDER BY clause,
to query optimization of a set data, making work easier with data
paging for anyone who writes in T-SQL as well for the entire Execution
Plan in SQL Server.
Below the T-SQL script with the same logic used in the previous
example.
--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE #PageNumber AS INT, #RowspPage AS INT
SET #PageNumber = 2
SET #RowspPage = 10
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((#PageNumber - 1) * #RowspPage) ROWS
FETCH NEXT #RowspPage ROWS ONLY;
TechNet: Paging a Query with SQL Server
MSDN: ROW_NUMBER (Transact-SQL)
Returns the sequential number of a row within a partition of a result set, starting at 1 for the first row in each partition.
The following example returns rows with numbers 50 to 60 inclusive in the order of the OrderDate.
WITH OrderedOrders AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber,
FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
FROM [dbo].[vSalesPerson]
)
SELECT RowNumber,
FirstName, LastName, Sales YTD
FROM OrderedOrders
WHERE RowNumber > 50 AND RowNumber < 60;
RowNumber FirstName LastName SalesYTD
--- ----------- ---------------------- -----------------
1 Linda Mitchell 4251368.54
2 Jae Pak 4116871.22
3 Michael Blythe 3763178.17
4 Jillian Carson 3189418.36
5 Ranjit Varkey Chudukatil 3121616.32
6 José Saraiva 2604540.71
7 Shu Ito 2458535.61
8 Tsvi Reiter 2315185.61
9 Rachel Valdez 1827066.71
10 Tete Mensa-Annan 1576562.19
11 David Campbell 1573012.93
12 Garrett Vargas 1453719.46
13 Lynn Tsoflias 1421810.92
14 Pamela Ansman-Wolfe 1352577.13
There is a good overview of different paging techniques at http://www.codeproject.com/KB/aspnet/PagingLarge.aspx
I've used ROWCOUNT method quite often mostly with SQL Server 2000 (will work with 2005 & 2008 too, just measure performance compared to ROW_NUMBER), it's lightning fast, but you need to make sure that the sorted column(s) have (mostly) unique values.
For SQL Server 2000 you can simulate ROW_NUMBER() using a table variable with an IDENTITY column:
DECLARE #pageNo int -- 1 based
DECLARE #pageSize int
SET #pageNo = 51
SET #pageSize = 20
DECLARE #firstRecord int
DECLARE #lastRecord int
SET #firstRecord = (#pageNo - 1) * #pageSize + 1 -- 1001
SET #lastRecord = #firstRecord + #pageSize - 1 -- 1020
DECLARE #orderedKeys TABLE (
rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
TableKey int NOT NULL
)
SET ROWCOUNT #lastRecord
INSERT INTO #orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate
SET ROWCOUNT 0
SELECT t.*
FROM Orders t
INNER JOIN #orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= #firstRecord
ORDER BY o.rownum
This approach can be extended to tables with multi-column keys, and it doesn't incur the performance overhead of using OR (which skips index usage). The downside is the amount of temporary space used up if the data set is very large and one is near the last page. I did not test cursor performance in that case, but it might be better.
Note that this approach could be optimized for the first page of data. Also, ROWCOUNT was used since TOP does not accept a variable in SQL Server 2000.
The best way for paging in sql server 2012 is by using offset and fetch next in a stored procedure.
OFFSET Keyword - If we use offset with the order by clause then the query will skip the number of records we specified in OFFSET n Rows.
FETCH NEXT Keywords - When we use Fetch Next with an order by clause only it will returns the no of rows you want to display in paging, without Offset then SQL will generate an error.
here is the example given below.
create procedure sp_paging
(
#pageno as int,
#records as int
)
as
begin
declare #offsetcount as int
set #offsetcount=(#pageno-1)*#records
select id,bs,variable from salary order by id offset #offsetcount rows fetch Next #records rows only
end
you can execute it as follow.
exec sp_paging 2,3
Use case wise the following seem to be easy to use and fast. Just set the page number.
use AdventureWorks
DECLARE #RowsPerPage INT = 10, #PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((#PageNumber-1)*#RowsPerPage)+1
AND #RowsPerPage*(#PageNumber)
also without CTE
use AdventureWorks
DECLARE #RowsPerPage INT = 10, #PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
) AS SOD
WHERE SOD.RowNum BETWEEN ((#PageNumber-1)*#RowsPerPage)+1
AND #RowsPerPage*(#PageNumber)
Try this approach:
SELECT TOP #offset a.*
FROM (select top #limit b.*, COUNT(*) OVER() totalrows
from TABLENAME b order by id asc) a
ORDER BY id desc;
These are my solutions for paging the result of query in SQL server side.
these approaches are different between SQL Server 2008 and 2012.
Also, I have added the concept of filtering and order by with one column. It is very efficient when you are paging and filtering and ordering in your Gridview.
Before testing, you have to create one sample table and insert some row in this table : (In real world you have to change Where clause considering your table fields and maybe you have some join and subquery in main part of select)
Create Table VLT
(
ID int IDentity(1,1),
Name nvarchar(50),
Tel Varchar(20)
)
GO
Insert INTO VLT
VALUES
('NAME' + Convert(varchar(10),##identity),'FAMIL' + Convert(varchar(10),##identity))
GO 500000
In all of these sample, I want to query 200 rows per page and I am fetching the row for page number 1200.
In SQL server 2008, you can use the CTE concept. Because of that, I have written two type of query for SQL server 2008+
-- SQL Server 2008+
DECLARE #PageNumber Int = 1200
DECLARE #PageSize INT = 200
DECLARE #SortByField int = 1 --The field used for sort by
DECLARE #SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE #FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE #FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE #FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
SELECT
Data.ID,
Data.Name,
Data.Tel
FROM
(
SELECT
ROW_NUMBER()
OVER( ORDER BY
CASE WHEN #SortByField = 1 AND #SortOrder = 'ASC'
THEN VLT.ID END ASC,
CASE WHEN #SortByField = 1 AND #SortOrder = 'DESC'
THEN VLT.ID END DESC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'ASC'
THEN VLT.Name END ASC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'DESC'
THEN VLT.Name END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'ASC'
THEN VLT.Tel END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'DESC'
THEN VLT.Tel END ASC
) AS RowNum
,*
FROM VLT
WHERE
( -- We apply the filter logic here
CASE
WHEN #FilterType = 'None' THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 1
AND VLT.ID = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 1
AND VLT.ID <> #FilterValue THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 2
AND VLT.Name = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 2
AND VLT.Name <> #FilterValue THEN 1
-- Tel column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 3
AND VLT.Tel = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 3
AND VLT.Tel <> #FilterValue THEN 1
END
) = 1
) AS Data
WHERE Data.RowNum > #PageSize * (#PageNumber - 1)
AND Data.RowNum <= #PageSize * #PageNumber
ORDER BY Data.RowNum
GO
And second solution with CTE in SQL server 2008+
DECLARE #PageNumber Int = 1200
DECLARE #PageSize INT = 200
DECLARE #SortByField int = 1 --The field used for sort by
DECLARE #SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE #FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE #FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE #FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
;WITH
Data_CTE
AS
(
SELECT
ROW_NUMBER()
OVER( ORDER BY
CASE WHEN #SortByField = 1 AND #SortOrder = 'ASC'
THEN VLT.ID END ASC,
CASE WHEN #SortByField = 1 AND #SortOrder = 'DESC'
THEN VLT.ID END DESC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'ASC'
THEN VLT.Name END ASC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'DESC'
THEN VLT.Name END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'ASC'
THEN VLT.Tel END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'DESC'
THEN VLT.Tel END ASC
) AS RowNum
,*
FROM VLT
WHERE
( -- We apply the filter logic here
CASE
WHEN #FilterType = 'None' THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 1
AND VLT.ID = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 1
AND VLT.ID <> #FilterValue THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 2
AND VLT.Name = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 2
AND VLT.Name <> #FilterValue THEN 1
-- Tel column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 3
AND VLT.Tel = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 3
AND VLT.Tel <> #FilterValue THEN 1
END
) = 1
)
SELECT
Data.ID,
Data.Name,
Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > #PageSize * (#PageNumber - 1)
AND Data.RowNum <= #PageSize * #PageNumber
ORDER BY Data.RowNum
-- SQL Server 2012+
DECLARE #PageNumber Int = 1200
DECLARE #PageSize INT = 200
DECLARE #SortByField int = 1 --The field used for sort by
DECLARE #SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE #FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE #FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE #FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
;WITH
Data_CTE
AS
(
SELECT
*
FROM VLT
WHERE
( -- We apply the filter logic here
CASE
WHEN #FilterType = 'None' THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 1
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.ID NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 1
AND VLT.ID = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 1
AND VLT.ID <> #FilterValue THEN 1
-- Name column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 2
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Name NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 2
AND VLT.Name = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 2
AND VLT.Name <> #FilterValue THEN 1
-- Tel column filter
WHEN #FilterType = 'Contain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'NotContain' AND #FilterColumn = 3
AND ( -- In this case, when the filter value is empty, we want to show everything.
VLT.Tel NOT LIKE '%' + #FilterValue + '%'
OR
#FilterValue = ''
) THEN 1
WHEN #FilterType = 'Match' AND #FilterColumn = 3
AND VLT.Tel = #FilterValue THEN 1
WHEN #FilterType = 'NotMatch' AND #FilterColumn = 3
AND VLT.Tel <> #FilterValue THEN 1
END
) = 1
)
SELECT
Data.ID,
Data.Name,
Data.Tel
FROM Data_CTE AS Data
ORDER BY
CASE WHEN #SortByField = 1 AND #SortOrder = 'ASC'
THEN Data.ID END ASC,
CASE WHEN #SortByField = 1 AND #SortOrder = 'DESC'
THEN Data.ID END DESC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'ASC'
THEN Data.Name END ASC,
CASE WHEN #SortByField = 2 AND #SortOrder = 'DESC'
THEN Data.Name END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'ASC'
THEN Data.Tel END ASC,
CASE WHEN #SortByField = 3 AND #SortOrder = 'DESC'
THEN Data.Tel END ASC
OFFSET #PageSize * (#PageNumber - 1) ROWS FETCH NEXT #PageSize ROWS ONLY;
From 2012 onward we can use
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
This is a duplicate of the 2012 old SO question:
efficient way to implement paging
FROM [TableX]
ORDER BY [FieldX]
OFFSET 500 ROWS
FETCH NEXT 100 ROWS ONLY
Here the topic is discussed in greater details, and with alternate approaches.
Well I have used the following sample query in my SQL 2000 database, it works well for SQL 2005 too. The power it gives you is dynamically order by using multiple columns.
I tell you ... this is powerful :)
ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary]
#CompanyID int,
#pageNumber int,
#pageSize int,
#sort varchar(200)
AS
DECLARE #sql nvarchar(4000)
DECLARE #strPageSize nvarchar(20)
DECLARE #strSkippedRows nvarchar(20)
DECLARE #strFields nvarchar(4000)
DECLARE #strFilter nvarchar(4000)
DECLARE #sortBy nvarchar(4000)
DECLARE #strFrom nvarchar(4000)
DECLARE #strID nvarchar(100)
If(#pageNumber < 0)
SET #pageNumber = 1
SET #strPageSize = CAST(#pageSize AS varchar(20))
SET #strSkippedRows = CAST(((#pageNumber - 1) * #pageSize) AS varchar(20))-- For example if pageNumber is 5 pageSize is 10, then SkippedRows = 40.
SET #strID = 'ListingDbID'
SET #strFields = 'ListingDbID,
ListingID,
[ExtraRoom]
'
SET #strFrom = ' vwListingSummary '
SET #strFilter = ' WHERE
CompanyID = ' + CAST(#CompanyID As varchar(20))
End
SET #sortBy = ''
if(len(ltrim(rtrim(#sort))) > 0)
SET #sortBy = ' Order By ' + #sort
-- Total Rows Count
SET #sql = 'SELECT Count(' + #strID + ') FROM ' + #strFROM + #strFilter
EXEC sp_executesql #sql
--// This technique is used in a Single Table pagination
SET #sql = 'SELECT ' + #strFields + ' FROM ' + #strFROM +
' WHERE ' + #strID + ' IN ' +
' (SELECT TOP ' + #strPageSize + ' ' + #strID + ' FROM ' + #strFROM + #strFilter +
' AND ' + #strID + ' NOT IN ' + '
(SELECT TOP ' + #strSkippedRows + ' ' + #strID + ' FROM ' + #strFROM + #strFilter + #SortBy + ') '
+ #SortBy + ') ' + #SortBy
Print #sql
EXEC sp_executesql #sql
The best part is sp_executesql caches later calls, provided you pass same parameters i.e generate same sql text.
CREATE view vw_sppb_part_listsource as
select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
select
part.SPPB_PART_ID
, 0 as is_rev
, part.part_number
, part.init_id
from t_sppb_init_part part
left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
where prev.SPPB_PART_ID is null
union
select
part.SPPB_PART_ID
, 1 as is_rev
, prev.part_number
, part.init_id
from t_sppb_init_part part
inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
) sppb_part
will restart idx when it comes to different init_id
For the ROW_NUMBER technique, if you do not have a sorting column to use, you can use the CURRENT_TIMESTAMP as follows:
SELECT TOP 20
col1,
col2,
col3,
col4
FROM (
SELECT
tbl.col1 AS col1
,tbl.col2 AS col2
,tbl.col3 AS col3
,tbl.col4 AS col4
,ROW_NUMBER() OVER (
ORDER BY CURRENT_TIMESTAMP
) AS sort_row
FROM dbo.MyTable tbl
) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row
This has worked well for me for searches over table sizes of even up to 700,000.
This fetches records 11 to 30.
create PROCEDURE SP_Company_List (#pagesize int = -1 ,#pageindex int= 0 ) > AS BEGIN SET NOCOUNT ON;
select Id , NameEn from Company ORDER by Id ASC
OFFSET (#pageindex-1 )* #pagesize ROWS FETCH NEXt #pagesize ROWS ONLY END GO
DECLARE #return_value int
EXEC #return_value = [dbo].[SP_Company_List] #pagesize = 1 , > #pageindex = 2
SELECT 'Return Value' = #return_value
GO
This bit gives you ability to paginate using SQL Server, and newer versions of MySQL and carries the total number of rows in every row.
Uses your pimary key to count number of unique rows.
WITH T AS
(
SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
, (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL
FROM TABLE (NOLOCK)
)
SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200
You didn't specify the language nor which driver you are using. Therefore I'm describing it abstractly.
Create a scrollable resultset / dataset. This required a primary on the table(s)
jump to the end
request the row count
jump to the start of the page
scroll through the rows until the end of the page