Reputation: 2096
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
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
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
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
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