带 TVP 参数的条件 WHERE 子句

Conditional WHERE clauses with TVP parameter

我正在构建一个食谱网站,我正在尝试创建一个带有 table 值参数的存储过程,但是 运行 遇到了一些问题。

所以 TVP 是根据用户 select 在尝试搜索食谱时的复选框列表来填充的。然后我想展示基于这些 select 离子的配方,因此我在我的存储过程中使用了 TVP。

但是,当用户没有select 复选框列表之一中的任何选项时,我希望查询显示所有记录(对于每个部分)。当我尝试执行此操作时,查询立即 return 没有结果。

TVP 设置和逻辑

类别 难度[​​=65=] 持续时间
肉类 简单 0-15 分钟
string.Empty 平均 30-45 分钟
困难 string.Empty

我希望程序根据类别、难度和持续时间中的所有选项 select 过滤每个食谱。 IE。在这种情况下,我希望存储过程 return 所有具有 Category = "Meat" 或 "Fish" 的食谱;难度 =“简单”或“平均”或“困难”,持续时间 =“0-15 分钟”或“30-45 分钟”。

例如,如果类别中不存在任何值,则存储过程应该return 来自所有类别的食谱,并且仅根据具有值的列进行过滤。

到目前为止的工作代码

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

仅当我在所有三个复选框列表(称为类别、难度和持续时间)中 select 选项时,此代码才有效。

我试过的代码#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

我试过的代码#2(只有类别的例子)

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 这两次尝试都没有结果。你能帮我吗?我错过了什么?

谢谢!

我将提出类似以下的建议 - 尽管正如 Dale 指出的那样,当您的 @TVP 值填充了不同的过滤器组合时,它会很方便地准确查看它的外观。但是,在此期间,请尝试一下,看看会得到什么结果。

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

基本上,我假设如果用户没有为特定过滤器(类别、难度或持续时间)勾选任何选项,那么@TVP table 中将没有行相应列中的值。 (如果这个假设不正确,那么我们将回到 Dale 的建议,即查看有关如何填充 @TVP 变量的更多详细信息 - 即您的应用程序端代码)