【问题标题】:How to exclude ISNULL from WHERE condition with multiple conditions?如何从具有多个条件的 WHERE 条件中排除 ISNULL?
【发布时间】:2022-01-20 09:06:16
【问题描述】:

我有以下数据:

DECLARE @tbl TABLE (
    Id INT,
    [Date_1] DATETIME2,
    [Date_2] DATETIME2
)

INSERT INTO @tbl ([Id], [Date_1], [Date_2]) VALUES
(1, '2018-08-15 16:05:20.000', NULL),
(2, '2018-08-15 16:05:20.000', NULL),
(3, '2018-08-15 16:05:20.000', NULL),
(4, '2018-08-20 00:00:00.000', NULL),
(5, NULL, NULL),
(6, '2018-08-30 14:02:08.000', '2018-09-05 12:31:30.530')

我有一个完美适用于各种条件的查询:

SELECT *
FROM @tbl AS t
WHERE
    (ISNULL(t.[Date_1], '1900-1-1') BETWEEN @DateFrom AND @DateTo) OR
    (ISNULL(t.[Date_2], '1900-1-1') BETWEEN @DateFrom AND @DateTo)

第一种情况

DECLARE @DateFrom DATE = '1900-01-01', @DateTo DATE = '9999-12-30' -- result one. The overall count is 6 rows

结果是:

第二种情况。如果我更改@DateFrom@DateTo

DECLARE @DateFrom DATE = '2018-08-15', @DateTo DATE = '2018-08-16' -- result two. The overall count is 3 rows

那么结果是:

第三种情况。如果我更改@DateFrom@DateTo

DECLARE @DateFrom DATE = '2018-09-05', @DateTo DATE = '2018-09-06' -- result three. The overall count is 1 row

那么结果是:

我的问题是如何从WHERE 语句中排除ISNULL? 我尝试了不同的方法,但结果不正确:

SELECT *
FROM @tbl AS t
WHERE
((t.[Date_1] IS NOT NULL AND t.[Date_1]  BETWEEN @DateFrom
    AND @DateTo) OR t.[Date_1] IS NULL) OR
((t.[Date_2] IS NOT NULL AND t.[Date_2] BETWEEN @DateFrom
    AND @DateTo) OR t.[Date_2] IS NULL)

任何帮助将不胜感激。我无法将@DateFrom@DateTo 设置为NULL

【问题讨论】:

  • NULL 是未知的,由程序员告诉 SQL 如何处理空值。那么,当日期为空时会发生什么?他们应该匹配还是不匹配?
  • @SalmanA NULL 不应匹配。我的意思是可以消除 ISNULL,但结果应该和我的问题一样?

标签: sql tsql datetime sql-server-2008


【解决方案1】:

首先,使用BETWEEN 比较日期时间数据类型是不正确的。例如,您的原始查询不允许您选择所有 2018-08-15 日期,但排除 2018-08-16 00:00:00 的确切值。

其次,您似乎想要一个“包罗万象”的查询。

我会使用以下 WHERE 子句:

WHERE Date_1 >= @DateFrom AND Date_1 < @DateTo
OR    Date_2 >= @DateFrom AND Date_2 < @DateTo
OR    @DateFrom IS NULL AND @DateTo IS NULL

然后设置如下值:

-- catch all
SET @DateFrom = NULL
SET @DateTo   = NULL

-- all dates between 2018-08-15 00:00 and 2018-08-15 23:59:59.9999999
SET @DateFrom = '2018-08-15'
SET @DateTo   = '2018-08-16'

-- all dates between 2018-09-05 00:00 and 2018-09-05 23:59:59.9999999
SET @DateFrom = '2018-09-05'
SET @DateTo   = '2018-09-06'

【讨论】:

  • 非常感谢您的回复,但是我无法编辑参数@DateFrom@DateTo。也许您还有其他建议?
  • 好的,输入(1900-01-01, 9999-12-30) 是否意味着包括所有行,包括空值?顺便说一句,日期的范围是 0001-01-01 到 9999-12-31。
  • 是的,你是对的!这意味着包括所有行
  • 在 where 子句中将此特殊情况硬编码为第三个条件 OR @DateFrom = '1900-01-01' AND @DateTo = '9999-12-30'
  • 您可以像之前或此处那样在 ISNULL 中硬编码日期。您的选择。
【解决方案2】:

当 (@DateFrom, @DateTo) 是最小和最大日期文字时,您似乎只想要所有记录。

然后这个查询返回那个。

SELECT  * 
FROM @tbl AS t     
WHERE ( (t.[Date_1] >= @DateFrom AND t.[Date_1] < DATEADD(day,1,@DateTo)) 
     OR (t.[Date_2] >= @DateFrom AND t.[Date_2] < DATEADD(day,1,@DateTo))
     OR (@DateFrom <= '1900-01-01' AND @DateTo >= '9999-01-01')
);

BETWEEN 已被替换,因为 Date_1 和 Date_2 不是 DATE 类型。

演示

DECLARE @tbl TABLE 
(
    Id int,
    [Date_1] DateTime2,
    [Date_2] DateTime2
)
Insert Into @tbl ([Id],[Date_1],[Date_2])
Values 
  (1, '2018-08-15 16:05:20.000', NULL)
, (2, '2018-08-15 16:05:20.000', NULL)
, (3, '2018-08-15 16:05:20.000', NULL)
, (4, '2018-08-20 00:00:00.000', NULL)
, (5, NULL,NULL)
, (6, '2018-08-30 14:02:08.000', '2018-09-05 12:31:30.530');

DECLARE @DateFrom DATE, @DateTo DATE;

SELECT @DateFrom = '1900-01-01', @DateTo = '9999-12-30'; 
-- result 1.  The overall count is 6 rows
    
SELECT  * 
FROM @tbl AS t     
WHERE ( (t.[Date_1] >= @DateFrom AND t.[Date_1] < DATEADD(day,1,@DateTo)) 
     OR (t.[Date_2] >= @DateFrom AND t.[Date_2] < DATEADD(day,1,@DateTo))
     OR (@DateFrom <= '1900-01-01' AND @DateTo >= '9999-01-01')
);
       
SELECT @DateFrom = '2018-08-15', @DateTo = '2018-08-16';
-- result two. The overall count is 3 rows   

SELECT  * 
FROM @tbl AS t     
WHERE ( (t.[Date_1] >= @DateFrom AND t.[Date_1] < DATEADD(day,1,@DateTo)) 
     OR (t.[Date_2] >= @DateFrom AND t.[Date_2] < DATEADD(day,1,@DateTo))
     OR (@DateFrom <= '1900-01-01' AND @DateTo >= '9999-01-01')
);

SET @DateFrom = '2018-09-05'; SET @DateTo = '2018-09-06';
-- result three. The overall count is 1 row

SELECT  * 
FROM @tbl AS t     
WHERE ( (t.[Date_1] >= @DateFrom AND t.[Date_1] < DATEADD(day,1,@DateTo)) 
     OR (t.[Date_2] >= @DateFrom AND t.[Date_2] < DATEADD(day,1,@DateTo))
     OR (@DateFrom <= '1900-01-01' AND @DateTo >= '9999-01-01')
);
GO
身份证 |日期_1 |日期_2 -: | :---------------------------- | :---------------------------- 1 | 2018-08-15 16:05:20.0000000 | 2 | 2018-08-15 16:05:20.0000000 | 3 | 2018-08-15 16:05:20.0000000 | 4 | 2018-08-20 00:00:00.0000000 | 5 | | 6 | 2018-08-30 14:02:08.0000000 | 2018-09-05 12:31:30.5300000 身份证 |日期_1 |日期_2 -: | :---------------------------- | :----- 1 | 2018-08-15 16:05:20.0000000 | 2 | 2018-08-15 16:05:20.0000000 | 3 | 2018-08-15 16:05:20.0000000 | 身份证 |日期_1 |日期_2 -: | :---------------------------- | :---------------------------- 6 | 2018-08-30 14:02:08.0000000 | 2018-09-05 12:31:30.5300000

db小提琴here

【讨论】:

  • 感谢您的回复。但是,对于图像中显示的所有三种情况,它给出的结果都不正确。
  • 对,还有那个。检查。
  • @Learner 已修复。查看更新。
  • 我刚刚注意到 Date_1 和 Date_2 是 DATETIME2。那么你不想使用BETWEEN。因为myDateTime BETWEEN '2022-01-20' AND '2022-01-22' 转换为myDateTime BETWEEN '2022-01-20 00:00:00' AND '2022-01-22 00:00:00'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 2019-08-25
  • 2020-12-13
  • 1970-01-01
相关资源
最近更新 更多