Martunis99
Martunis99

Reputation: 21

Conditional WHERE clauses with TVP parameter

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

Answers (1)

Craig
Craig

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

Related Questions