Optimizing sql query in nopcommerce
I am optimizing a sql query and I welcome advice on how to improve this query. It is part of nopcommerce's free solution to download products in categories. At this time, it takes about 8-9 seconds to load the page, we can bring this to 3-4 seconds if we can.
Here is a request, I welcome every piece of advice on how I can improve this:
ALTER PROCEDURE [dbo].[Nop_ProductLoadForCategory]
(
@CategoryID int = 0,
@manufacturerId int = 0,
@PageIndex int = 0,
@PageSize int = 2147483644,
@TotalRecords int = null OUTPUT,
@VehiclesYear varchar(4) = null,
@VehiclesMake varchar(20) = null,
@VehiclesModel varchar(50) =null,
@VehiclesSubmodel varchar(50) =null,
@Universal bit = 1
)
AS
SET NOCOUNT ON
BEGIN
--paging
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int
SET @RowsToReturn = @PageSize * (@PageIndex + 1)
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1
CREATE TABLE #DisplayOrderTmp
(
[ID] int IDENTITY (1, 1) NOT NULL,
[ProductID] int NOT NULL
)
if isnull(@manufacturerId, 0) = 0
if @Universal = 1
INSERT INTO #DisplayOrderTmp ([ProductID])
select a.ProductID from (
SELECT TOP 100 PERCENT
row_number() over(order by pv.DisplayOrder) as ID,
p.ProductID
FROM dbo.Nop_Product p with (NOLOCK)
INNER JOIN Nop_Product_Category_Mapping pcm with (NOLOCK) ON p.ProductID=pcm.ProductID
LEFT OUTER JOIN Nop_ProductVariant pv with (NOLOCK) ON pv.ProductID = p.ProductId
left outer join WC_ProductVehicleApplications pva with (NOLOCK) on pva.ProductID=p.ProductId
left outer join [WC_Vehicles] v with (NOLOCK) on pva.VehicleID = v.VehicleID
WHERE
(pcm.CategoryID=@CategoryID)
AND (p.Published = 1)
AND (p.Deleted=0)
and (((@VehiclesYear between [YEAR] and YearEnd or @VehiclesYear is null)
and ([Make] = @VehiclesMake or @VehiclesMake is null)
and ([Model] = @VehiclesModel or @VehiclesModel is null)
and ([SubModel] = @VehiclesSubmodel or @VehiclesSubmodel is null))
or p.IsUniversal = 1)
) a
GROUP BY
ProductID
ORDER BY
min([ID])
else
INSERT INTO #DisplayOrderTmp ([ProductID])
select a.ProductID from (
SELECT TOP 100 PERCENT
row_number() over(order by pv.DisplayOrder) as ID,
p.ProductID
FROM dbo.Nop_Product p with (NOLOCK)
INNER JOIN Nop_Product_Category_Mapping pcm with (NOLOCK) ON p.ProductID=pcm.ProductID
LEFT OUTER JOIN Nop_ProductVariant pv with (NOLOCK) ON pv.ProductID = p.ProductId
left outer join WC_ProductVehicleApplications pva with (NOLOCK) on pva.ProductID=p.ProductId
left outer join [WC_Vehicles] v with (NOLOCK) on pva.VehicleID = v.VehicleID
WHERE
(pcm.CategoryID=@CategoryID)
AND (p.Published = 1)
AND (p.Deleted=0)
and (((@VehiclesYear between [YEAR] and YearEnd or @VehiclesYear is null)
and ([Make] = @VehiclesMake or @VehiclesMake is null)
and ([Model] = @VehiclesModel or @VehiclesModel is null)
and ([SubModel] = @VehiclesSubmodel or @VehiclesSubmodel is null))
and p.IsUniversal <> 1)
) a
GROUP BY
ProductID
ORDER BY
min([ID])
else
INSERT INTO #DisplayOrderTmp ([ProductID])
select a.ProductID from (
SELECT TOP 100 PERCENT
row_number() over(order by pv.DisplayOrder) as ID,
p.ProductID
FROM dbo.Nop_Product p with (NOLOCK)
INNER JOIN Nop_Product_Category_Mapping pcm with (NOLOCK) ON p.ProductID=pcm.ProductID
LEFT OUTER JOIN Nop_ProductVariant pv with (NOLOCK) ON pv.ProductID = p.ProductId
left outer join WC_ProductVehicleApplications pva with (NOLOCK) on pva.ProductID=p.ProductId
left outer join [WC_Vehicles] v with (NOLOCK) on pva.VehicleID = v.VehicleID
WHERE
(pcm.CategoryID=@CategoryID)
AND (p.Published = 1)
AND (p.Deleted=0)
and (((@VehiclesYear between [YEAR] and YearEnd or @VehiclesYear is null)
and ([Make] = @VehiclesMake or @VehiclesMake is null)
and ([Model] = @VehiclesModel or @VehiclesModel is null)
and ([SubModel] = @VehiclesSubmodel or @VehiclesSubmodel is null))
or p.IsUniversal = 1)
) a
GROUP BY
ProductID
ORDER BY
min([ID])
--CREATE UNIQUE NONCLUSTERED INDEX IX_1 on #DisplayOrderTmp ([ID], [ProductID])
--CREATE TABLE #PageIndex
--(
-- [IndexID] int IDENTITY (1, 1) NOT NULL,
-- [ProductID] int NOT NULL
--)
--INSERT INTO #PageIndex ([ProductID])
--SELECT TOP 100 PERCENT
-- Row_Number() Over(Order By min([ID])) as RowNum, ProductID
--FROM #DisplayOrderTmp with (NOLOCK)
--GROUP BY ProductID
--ORDER BY min([ID])
--select ProductID
--FROM #DisplayOrderTmp with (NOLOCK)
--GROUP BY ProductID
--SELECT
-- ProductID
--FROM
-- #DisplayOrderTmp with (NOLOCK)
--GROUP BY
-- ProductID
--ORDER BY
-- min([ID])
--select ProductID from #DisplayOrderTmp with (NOLOCK) --order by min([ID])
--CREATE UNIQUE NONCLUSTERED INDEX IX_2 on #PageIndex ([IndexID], [ProductID])
--total records
SET @TotalRecords = @@rowcount
SET ROWCOUNT @RowsToReturn
--DROP TABLE #DisplayOrderTmp
--return
SELECT
p.ProductId,
p.Name,
p.ShortDescription,
p.FullDescription,
p.AdminComment,
p.TemplateId,
p.ShowOnHomePage,
p.MetaKeywords,
p.MetaDescription,
p.MetaTitle,
p.SEName,
p.AllowCustomerReviews,
p.AllowCustomerRatings,
p.RatingSum,
p.TotalRatingVotes,
p.Published,
p.Deleted,
p.CreatedOn,
p.UpdatedOn,
p.[IsUniversal],
p.FullDescriptionSave
FROM
--(SELECT TOP 100 PERCENT
-- Row_Number() Over(Order By min([ID])) as RowNum, ProductID
--FROM #DisplayOrderTmp with (NOLOCK)
--GROUP BY ProductID
--ORDER BY min([ID])
--) [pi]
--inner join
#DisplayOrderTmp [pi]
--on dot.ProductID = [pi].ProductID
INNER JOIN Nop_Product p with (NOLOCK) on p.ProductID = [pi].ProductID
INNER JOIN Nop_Product_Category_Mapping pcm with (NOLOCK) ON p.ProductID=pcm.ProductID
WHERE
--[pi].IndexID > @PageLowerBound AND
--[pi].IndexID < @PageUpperBound
[pi].ID > @PageLowerBound AND
[pi].ID < @PageUpperBound
ORDER BY
[pi].ID
SET ROWCOUNT 0
DROP TABLE #DisplayOrderTmp
END
Thanks in advance, Laziale
source to share
OK, so you have 45,000 views and 2,000 CPUs. These numbers are generally high and the query can be optimized. The Profiler screenshot is cropped. How many rows are returned?
Can you change your question and post the execution plan?
Note, however, that SQL only considers the query to take 651 milliseconds . If you see a delay of 8-9 seconds, the problem is most likely in the client code. This is the first place I would spend optimizing time (unless your query is returning row gradients that are discarded on the client).
source to share
NopCommerce is deadly slow. If you have more than a few thousand products, you already have problems. You will need to do heavy caching for the home page, Home category, and product listing page.
We have the same problem, after implementing the cache, we reduce the load time to 1.5-2 seconds from 8-10 seconds. We tested Nop Commerce 2.2 / 2.3 / 2.4 / 2.5 under load, but frankly, the performance improvement is very marginal in 2.5 and you should look at aggressive caching to improve site performance. Without the cache, your site will see huge problems with only a few hundred concurrent requests.
EBarr is right, not SQL which is slow, the application is also very slow if you profile it with any good profiler. Please note that NopCommerce already has a mini profiler that you can activate from the admin side.
Update October 30, 2015
A number of new versions of nopCommerce have been released since this answer was originally written and has improved significantly in terms of out-of-the-box performance.
In addition, we have integrated nopCommerce with Apache Solr, so nopCommerce can be used for huge sites with millions of products and visitors, with a faster navigation directory page, faster facets, and an improved and faster drill search. The integration is done as a standard plugin popular as nopAccelerate http://www.nopaccelerate.com/
source to share