izip
izip

Reputation: 2096

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

Upvotes: 0

Views: 2115

Answers (4)

izip
izip

Reputation: 2096

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.

Upvotes: 0

Ron Weston
Ron Weston

Reputation: 290

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

Upvotes: 1

SQLMenace
SQLMenace

Reputation: 135081

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

Upvotes: 0

Ron Weston
Ron Weston

Reputation: 290

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.

Upvotes: 1

Related Questions