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

标签 sql sql-server sql-server-2014

我一直使用 COALESCE() 生成动态 WHERE 语句,以返回传入的值或返回所有记录。

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

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 

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

Give me all the rows in the vehicle table where the YEAR part of the RegistrationDate is less than the @MaxAge or the YEAR part of the ProductionDate is less than the @MaxAge. If @MaxAge is NULL, then give me the whole lot no matter when it was made or registered.

有时,如果车辆尚未注册,表中的 RegistrationDate 值将为 NULL。有时,如果卖家根本不知道生产日期,ProductionDate 可以为 NULL。

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

最佳答案

你可以稍微简化一下:

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掉进去,你就会错过这个记录。因此,我已从答案中删除该声明。

关于sql - 如何使用具有 OR 子句的 COALESCE 创建动态 WHERE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37446864/

相关文章:

sql - 如何可靠地获取查询的 SQL_ID

mysql - 如何在MySQL中克服 "Result consisted more than one row"

sql - 检测列中导致 SQL 错误的单元格

c# - asp.net 和 c# 中的动态下拉列表

sql-server - 如何根据键对数据进行 UNION 而不重复?

sql-server - 如何对由任意表达式描述的日期排序事件进行分组/窗口?

php - 使用 PHP/MySQL 生成主从 HTML 表(堆叠和嵌入)

sql - 这可以在 SQL 2008 中完成吗?

sql - 返回不包含特定列的所有表的查询

sql-server - 无法将空字符串传递到非空数据库字段