How to exclude ISNULL from WHERE condition with multiple conditions?

I have the following data:

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')

And I have a query which perfectly works with various conditions:

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)

The first case.

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

Result would be:

enter image description here

The second case. If I change @DateFrom and @DateTo:

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

Then result would be:

enter image description here

The third case. If I change @DateFrom and @DateTo:

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

Then result would be:

enter image description here

My question is how can it possible to exclude ISNULL from WHERE statement? I've tried different approaches, but they give me incorrect results:

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)

Any help would be greatly appreciated. I cannot set @DateFrom and @DateTo to NULL.


Solution 1:

First off, using BETWEEN to compare datetime datatype is incorrect. For example your original query does not allow you to select all 2018-08-15 dates but exclude the exact value of 2018-08-16 00:00:00.

Secondly it looks like you want a "catch all" query.

I would use the following WHERE clause:

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

Then set the values as follows:

-- 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'

Solution 2:

It looks like you just want all records when (@DateFrom, @DateTo) are the minimum & maximum date literals.

Then this query returns that.

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')
);

The BETWEEN were replaced since Date_1 and Date_2 aren't DATE types.

Demo

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
Id | Date_1                      | Date_2                     
-: | :-------------------------- | :--------------------------
 1 | 2018-08-15 16:05:20.0000000 | null                       
 2 | 2018-08-15 16:05:20.0000000 | null                       
 3 | 2018-08-15 16:05:20.0000000 | null                       
 4 | 2018-08-20 00:00:00.0000000 | null                       
 5 | null                        | null                       
 6 | 2018-08-30 14:02:08.0000000 | 2018-09-05 12:31:30.5300000

Id | Date_1                      | Date_2
-: | :-------------------------- | :-----
 1 | 2018-08-15 16:05:20.0000000 | null  
 2 | 2018-08-15 16:05:20.0000000 | null  
 3 | 2018-08-15 16:05:20.0000000 | null  

Id | Date_1                      | Date_2                     
-: | :-------------------------- | :--------------------------
 6 | 2018-08-30 14:02:08.0000000 | 2018-09-05 12:31:30.5300000

db<>fiddle here