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

vol*_*one 2 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 
Run Code Online (Sandbox Code Playgroud)

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

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

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

解决这个问题的最佳方法是什么?我已经尝试过查看SQL书籍和在线,但是找不到任何可以匹配这种情况的东西.

lev*_*man 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
Run Code Online (Sandbox Code Playgroud)

请注意,如果@MaxAge为null,则两个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
Run Code Online (Sandbox Code Playgroud)

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

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

这可以追溯到 @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
Run Code Online (Sandbox Code Playgroud)

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

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