Reputation: 21
I am building a recipe website and I am trying to create a stored procedure with a table-valued parameter but am running into some issues.
So the TVP is filled based on the checkboxlists that the user selects when he is trying to search for a recipe. I then would like to show recipes based on those selections, and for that reason I used a TVP in my stored procedure.
However I would like for the query to display all records (for each section) when the user does not select any options in one of the checkboxlists. When I try to do this the query immediately returns no results.
TVP Setup and Logic
Category | Difficulty | Duration |
---|---|---|
Meat | Easy | 0-15 min |
string.Empty | Average | 30-45 min |
Fish | Hard | string.Empty |
I want the procedure to filter each recipe by all the options selected in Category, Difficulty and Duration. I.e. in this case, I want the stored procedure to return all recipes that have Category = "Meat" or "Fish"; that have Difficulty = "Easy" or "Average" or "Hard" and have Duration = "0-15min" or "30-45min".
In case no values exist in Category, for instance, the stored procedure should return recipes from all categories, and only filter based on the columns that have values.
Working code so far
ALTER PROCEDURE uspPesquisarReceita (@TVP dbo.Filters READONLY)
AS
BEGIN
SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration, R.Rating, R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1
AND (C.Category IN (SELECT Category FROM @TVP)
AND DF.Dificulty IN (SELECT Dificulty FROM @TVP)
AND D.Duration IN (SELECT Duration FROM @TVP))
END
This code is working, only when I select options in all three of the checkboxlists (called Category, Dificulty and Duration).
Code I have tried #1
ALTER PROCEDURE uspPesquisarReceita (@TVP dbo.Filters READONLY)
AS
BEGIN
SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration, R.Rating, R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1
AND (C.Category IN (CASE WHEN NOT EXISTS (SELECT Category FROM @TVP)
THEN
(SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration,
R.Rating, R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1)
ELSE (SELECT Category FROM @TVP)
AND DF.Dificulty IN (SELECT Dificulty FROM @TVP)
*same as above*
AND D.Duration IN (SELECT Duration FROM @TVP))
*same as above*
END
Code I have tried #2 (only with example for Category)
ALTER PROCEDURE uspPesquisarReceita (@TVP dbo.Filters READONLY)
AS
BEGIN
IF NOT EXISTS (SELECT Category FROM @TVP)
SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration, R.Rating,
R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1
AND DF.Dificulty IN (SELECT Dificulty FROM @TVP)
AND D.Duration IN (SELECT Duration FROM @TVP))
ELSE
SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration, R.Rating,
R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1
AND (C.Category IN (SELECT Category FROM @TVP)
AND DF.Dificulty IN (SELECT Dificulty FROM @TVP)
AND D.Duration IN (SELECT Duration FROM @TVP))
END
None of these two attempts have yielded any results. Can you help me out? What am I missing?
Thank you!
Upvotes: 0
Views: 397
Reputation: 1226
I'm going to suggest something like the following - although, as Dale points out, it would be kind of handy to see exactly how your @TVP value looks when it's populated with different combinations of filters. However, in the interim, give this a try and see what results you get.
ALTER PROCEDURE uspPesquisarReceita (@TVP dbo.Filters READONLY)
AS
BEGIN
SELECT R.RecipeName, C.Category, DF.Dificulty, D.Duration, R.Rating, R.Publication, R.Photo, R.Views, R.IDRecipe
FROM dbo.Recipes R
LEFT JOIN dbo.Categories C ON R.IDCategory = C.IDCategory
LEFT JOIN dbo.Dificulty DF ON R.IDDificulty = DF.IDDificulty
LEFT JOIN dbo.Duration D ON R.IDDuration = D.IDDuration
WHERE R.IDStatus = 1
AND
(
(C.Category IN (SELECT Category FROM @TVP))
OR
(NOT EXISTS (SELECT 1 FROM @TVP WHERE LTRIM(ISNULL(Category, '')) <> ''))
)
AND
(
(DF.Dificulty IN (SELECT Dificulty FROM @TVP))
OR
(NOT EXISTS (SELECT 1 FROM @TVP WHERE LTRIM(ISNULL(Dificulty, '')) <> ''))
)
AND
(
(D.Duration IN (SELECT Duration FROM @TVP))
OR
(NOT EXISTS (SELECT 1 FROM @TVP WHERE LTRIM(ISNULL(Duration, '')) <> ''))
)
END
Basically, I'm assuming that if a user hasn't ticked any options for a particular filter (Category, Difficulty or Duration), then there will be no rows in the @TVP table with a value in the corresponding column. (If this assumption is incorrect, then we are back to Dale's suggestion of seeing more detail on how you are populating the @TVP variable - ie. your app-side code)
Upvotes: 3