我总是使用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书籍和网上查找,但无法找到符合这种情况的任何内容。
答案 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
)