如何使用具有OR子句的COALESCE创建动态WHERE

时间:2016-05-25 20:18:55

标签: sql sql-server sql-server-2014

我总是使用WHERE创建动态COALESCE()语句,以返回传入的值或返回所有记录。

但是当我需要检查两列时,我似乎无法做到这一点,如果没有传入参数则返回所有记录。这是我的代码片段:

DECLARE @MaxAge INT = NULL  --- Example value: 5

SELECT
    *
FROM
    dbo.Vehicle
WHERE
DATEPART(YEAR, RegistrationDate) <= 
CASE 
    WHEN @MaxAge IS NOT NULL
    THEN (DATEPART(YEAR,GETDATE()) - @MaxAge)
    ELSE DATEPART(YEAR, RegistrationDate)
END 
OR
DATEPART(YEAR, ProductionDate) <= 
CASE 
    WHEN @MaxAge IS NOT NULL
    THEN (DATEPART(YEAR,GETDATE()) - @MaxAge)
    ELSE DATEPART(YEAR, ProductionDate)
END 

我尝试做的事(用简单的英语)是:

  

给我车辆表格中注册日期的YEAR部分的所有行   小于@MaxAge或者ProductionDate的YEAR部分小于   @MaxAge。如果@MaxAge为NULL,那么无论何时制作或注册,都要全部给我。

如果车辆尚未注册,有时表中的RegistrationDate值将为NULL。如果卖家根本不知道何时制作,ProductionDate有时可能为空。

解决这个问题的最佳方法是什么?我曾尝试过在SQL书籍和网上查找,但无法找到符合这种情况的任何内容。

2 个答案:

答案 0 :(得分:5)

您可以稍微简化一下:

SELECT  *
FROM    dbo.Vehicle
WHERE   @MaxAge IS NULL -- return everything if null
OR      DATEDIFF(YEAR, RegistrationDate, GETDATE()) <= @MaxAge  
OR      DATEDIFF(YEAR, ProductionDate, GETDATE()) <= @MaxAge

请注意,如果@MaxAge为空,则两个OR ... <= @MaxAge语句都将返回false。你仍然会得到WHERE @MaxAge IS NULL的所有内容。

我在此假设@MaxAge代表您关注的最早注册。 MaxAge为5时,您将在'01/01/2011'之后或之后返回所有结果。

编辑:

根据ErikE的评论,请注意DATEDIFF()有一些你可能没想到的奇怪行为。

考虑这两个陈述是等价的:

SELECT  DATEDIFF(YEAR, '01/01/2011', GETDATE()) -- = 5
SELECT  DATEDIFF(YEAR, '12/31/2011', GETDATE()) -- = 5

要解决此问题,您可以尝试:

SELECT  *
FROM    dbo.Vehicle
WHERE   @MaxAge IS NULL -- return everything if null
OR      DATEADD(YEAR, -@MaxAge, GETDATE()) <= RegistrationDate
OR      DATEADD(YEAR, -@MaxAge, GETDATE()) <= ProductionDate

从今天开始返回 @MaxAge年(包括当前时间)并返回之后发生的所有事情。在这种情况下,5年前和10秒前发生的任何事情都将被排除在外。要解决此问题,您可以使用:

DECLARE @TodayNoTime datetime = DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0) -- 05/25/2016 00:00:00.000

SELECT  *
FROM    dbo.Vehicle
WHERE   @MaxAge IS NULL -- return everything if null
OR      DATEADD(YEAR, -@MaxAge, @TodayNoTime) <= RegistrationDate -- note the negative sign
OR      DATEADD(YEAR, -@MaxAge, @TodayNoTime) <= ProductionDate

这将返回今天@MaxAge年前发生的任何事情,无论时间如何。

最后,在这种情况下使用COALESCE()不会起作用,因为您只会比较一列或另一列,无论哪一个先发现非空。因此,如果RegistrationDate超出您的参数范围,但ProductionDate落入其中,您将错过此记录。因此,我已从答案中删除了该陈述。

答案 1 :(得分:2)

OR子句中添加WHERE似乎会返回正确的值

DECLARE @MaxAge INT = NULL

SELECT * 
FROM Vehicle 
WHERE @MaxAge IS NULL OR 
    (DATEPART(YEAR, RegistrationDate) <= 
           CASE WHEN @MaxAge IS NOT NULL
           THEN (DATEPART(YEAR,GETDATE()) - @MaxAge)
           ELSE DATEPART(YEAR, RegistrationDate) END 
OR 
     DATEPART(YEAR, ProductionDate) <= 
               CASE WHEN @MaxAge IS NOT NULL
                    THEN (DATEPART(YEAR,GETDATE()) - @MaxAge)
                    ELSE DATEPART(YEAR, ProductionDate)  END 
    )