SQL Server: how to avoid between operator with datetime column? - sql

I have a datetime column and I would like to select rows based on this columns. My query is
SELECT *
FROM dbo.mytable
WHERE daycol BETWEEN '20110404' AND '20110406';
This will also brings me a row
2011-04-06 00:00:00.000
Which is not correct. How to avoid this? Should between operator be avoided with datetime columns?

You may use this if you don't want the row of 2011-04-06 00:00:00.000 date
SELECT *
FROM dbo.mytable
WHERE daycol >= '20110404' AND daycol < '20110406';

If you write out this line:
WHERE daycol BETWEEN '20110404' AND '20110406';
You get:
WHERE '2011-04-04 00:00:00.000' <= daycol
and daycol <= '2011-04-06 00:00:00.000'
Between is inclusive. If you'd like to exclude the boundaries, change <= to < in the expanded version.

Please note this is the expected behavior of BETWEEN
(See: http://msdn.microsoft.com/en-us/library/ms187922.aspx)
From the article:
BETWEEN returns TRUE if the value of test_expression is greater than or equal to the value of begin_expression and less than or equal to the value of end_expression.
You can modify your end_expression to get different results than you are receiving.
Or use >=, < operators. But be careful if the column you are comparing against is DATETIME T-SQL will convert '20110406' to '2011-04-06 00:00:00.000' so take this into consideration when determining which operators to use.

Related

Why is '=' operator not working on comparing Datetime?

I am trying to fetch records only of the current day. I have used the = operator to compare the date but it is not working, if I subtract 1 from the current date and then use >= operator then it works.
I am putting both queries here.
This code works:
1 = (
CASE WHEN ISNULL(DO.CREATEDON, '') >= GETDATE()-1
THEN 1
END
This code doesn't work:
1 = (
CASE WHEN ISNULL(DO.CREATEDON, '') = GETDATE()
THEN 1
END
Why is the latter code not working?
GETDATE() returns a datetime, so (for me), right now, using GETDATE() returns something like 2021-01-19T09:43:27.123. It's therefore very unlikely the value in your column CREATEDON is going to be the same exact time that GETDATE() returns, accurate to the nearest 1/300 of a second.
If your column CREATEDON is a date and time value, use inclusive date ranges:
WHERE DO.CREATEDON >= CONVERT(date,GETDATE())
AND DO.CREATEDON < CONVERT(date,DATEADD(DAY, 1, GETDATE())
If CREATEDON is a date, then just CONVERT the value of GETDATE() to a date:
WHERE DO.CREATEDON = CONVERT(date,GETDATE())
Note that there's no need for the ISNULL, as is the value of NULL it is by definition not the value of GETDATE(). Also, converting'' to a date and time value is a little odd; but it would convert to 1900-01-01, which again, is not today. Adding an ISNULL on DO.CREATEDON in the WHERE will only harm the performance; don't do it.

SQL Server: Inconsistencies in date

create table #temp
(
A date
)
insert into #temp(A)
values(GETDATE())
insert into #temp(A)
values(GETDATE()-1)
Now when I query the table as
select A from #temp where A>=GETDATE() and A<=GETDATE()
I get no records
But GETDATE() record value should satisfy my where condition, shouldn't it at least pass me one record?
Please guide me if I am missing some point here.
You need to do conversion, so it seems :
where a >= convert(date, dateadd(day, -1, getdate())) and
a <= convert(date, getdate());
Your where clause comparing date as :
where a >= '2020-04-21 16:01:27.277' and a <= '2020-04-21 16:01:27.277'
So, you need to convert date because getdate()will also return time portions.
Since your where clause looks for single day so you can do :
where a = convert(date, getdate())
Yogesh is right.
GETDATE() gives the present DATEIME value. When you insert it into a DATE column, SQL Server coerces -- silently typecasts -- the DATETIME to a DATE before inserting it.
But when you use it in a WHERE clause, SQL Server coerces the DATE from your column into a DATETIME value by turning 2020-04-20 into 2020-04-20 00:00:00. That can't be the same as GETDATE() except during the first few milliseconds of each day. (Meaning you or your test krewe are extremely unlikely to catch it equal.)

SQL Server : today is not equal to today

Maybe I'm making an obvious mistake but can anyone explain what's going on here? I was running a query where the table's field is datetime and the query I was running was something like
SELECT *
FROM Table
WHERE DateTimeColumn <= '20170714'
and I noticed the output excluded the records where DateTimeColumn is '20170714' they finished at '20170713'
Below I was expecting all 3 IIF to fall into true.
DECLARE #d1 DATE = '20170714'
SELECT IIF(GETDATE() <= #d1, 'GETDATE() Less than or equal to #d1', 'GETDATE() **NOT** Less than or equal to #d1')
DECLARE #d2 DATE = '20170714 11:59:59'
SELECT IIF(GETDATE() <= #d2, 'GETDATE() Less than or equal to #d2', 'GETDATE() **NOT** Less than or equal to #d2')
DECLARE #tomorrow DATE = '20170715'
SELECT IIF(GETDATE() <= #tomorrow, 'GETDATE() Less than or equal to #tomorrow', 'GETDATE() **NOT** Less than or equal to #tomorrow')
Just use less than 2017-07-15 (tomorrow)
SELECT *
FROM Table
WHERE DateTimeColumn < '20170715'
If wanting to use getdate, try this:
SELECT *
FROM Table
WHERE DateTimeColumn < dateadd(day,1,cast(getdate() as date))
Use sargable predicates. DO NOT convert your data to suit a filtering predicate, this affects index access and/or requires unnecessary calculations. Here is a former answer on the similar question.
Also note that 23:59:59 is NOT the end of a day, it is one full second short of a full day: datetime is accurate to approx 3 milliseconds and datetime2 is even more sensitive.
Can you just change the query like this :
SELECT *
FROM Table
WHERE CONVERT(date, DateTimeColumn) <= '20170714'
It would return all the records less that 14 and record with date 14.

Compare DATETIME and DATE ignoring time portion

I have two tables where column [date] is type of DATETIME2(0).
I have to compare two records only by theirs Date parts (day+month+year), discarding Time parts (hours+minutes+seconds).
How can I do that?
Use the CAST to the new DATE data type in SQL Server 2008 to compare just the date portion:
IF CAST(DateField1 AS DATE) = CAST(DateField2 AS DATE)
A small drawback in Marc's answer is that both datefields have been typecast, meaning you'll be unable to leverage any indexes.
So, if there is a need to write a query that can benefit from an index on a date field, then the following (rather convoluted) approach is necessary.
The indexed datefield (call it DF1) must be untouched by any kind of function.
So you have to compare DF1 to the full range of datetime values for the day of DF2.
That is from the date-part of DF2, to the date-part of the day after DF2.
I.e. (DF1 >= CAST(DF2 AS DATE)) AND (DF1 < DATEADD(dd, 1, CAST(DF2 AS DATE)))
NOTE: It is very important that the comparison is >= (equality allowed) to the date of DF2, and (strictly) < the day after DF2. Also the BETWEEN operator doesn't work because it permits equality on both sides.
PS: Another means of extracting the date only (in older versions of SQL Server) is to use a trick of how the date is represented internally.
Cast the date as a float.
Truncate the fractional part
Cast the value back to a datetime
I.e. CAST(FLOOR(CAST(DF2 AS FLOAT)) AS DATETIME)
Though I upvoted the answer marked as correct. I wanted to touch on a few things for anyone stumbling upon this.
In general, if you're filtering specifically on Date values alone. Microsoft recommends using the language neutral format of ymd or y-m-d.
Note that the form '2007-02-12' is considered language-neutral only
for the data types DATE, DATETIME2, and DATETIMEOFFSET.
To do a date comparison using the aforementioned approach is simple. Consider the following, contrived example.
--112 is ISO format 'YYYYMMDD'
declare #filterDate char(8) = CONVERT(char(8), GETDATE(), 112)
select
*
from
Sales.Orders
where
CONVERT(char(8), OrderDate, 112) = #filterDate
In a perfect world, performing any manipulation to the filtered column should be avoided because this can prevent SQL Server from using indexes efficiently. That said, if the data you're storing is only ever concerned with the date and not time, consider storing as DATETIME with midnight as the time. Because:
When SQL Server converts the literal to the filtered column’s type, it
assumes midnight when a time part isn’t indicated. If you want such a
filter to return all rows from the specified date, you need to ensure
that you store all values with midnight as the time.
Thus, assuming you are only concerned with date, and store your data as such. The above query can be simplified to:
--112 is ISO format 'YYYYMMDD'
declare #filterDate char(8) = CONVERT(char(8), GETDATE(), 112)
select
*
from
Sales.Orders
where
OrderDate = #filterDate
You can try this one
CONVERT(DATE, GETDATE()) = CONVERT(DATE,'2017-11-16 21:57:20.000')
I test that for MS SQL 2014 by following code
select case when CONVERT(DATE, GETDATE()) = CONVERT(DATE,'2017-11-16 21:57:20.000') then 'ok'
else '' end
You may use DateDiff and compare by day.
DateDiff(dd,#date1,#date2) > 0
It means #date2 > #date1
For example :
select DateDiff(dd, '01/01/2021 10:20:00', '02/01/2021 10:20:00')
has the result : 1
For Compare two date like MM/DD/YYYY to MM/DD/YYYY .
Remember First thing column type of Field must be dateTime.
Example : columnName : payment_date dataType : DateTime .
after that you can easily compare it.
Query is :
select * from demo_date where date >= '3/1/2015' and date <= '3/31/2015'.
It very simple ......
It tested it.....

Does MS SQL Server's "between" include the range boundaries?

For instance can
SELECT foo
FROM bar
WHERE foo BETWEEN 5 AND 10
select 5 and 10 or they are excluded from the range?
The BETWEEN operator is inclusive.
From Books Online:
BETWEEN returns TRUE if the value of
test_expression is greater than or
equal to the value of begin_expression
and less than or equal to the value of
end_expression.
DateTime Caveat
NB: With DateTimes you have to be careful; if only a date is given the value is taken as of midnight on that day; to avoid missing times within your end date, or repeating the capture of the following day's data at midnight in multiple ranges, your end date should be 3 milliseconds before midnight on of day following your to date. 3 milliseconds because any less than this and the value will be rounded up to midnight the next day.
e.g. to get all values within June 2016 you'd need to run:
where myDateTime between '20160601' and DATEADD(millisecond, -3, '20160701')
i.e.
where myDateTime between '20160601 00:00:00.000' and '20160630 23:59:59.997'
datetime2 and datetimeoffset
Subtracting 3 ms from a date will leave you vulnerable to missing rows from the 3 ms window. The correct solution is also the simplest one:
where myDateTime >= '20160601' AND myDateTime < '20160701'
Yes, but be careful when using between for dates.
BETWEEN '20090101' AND '20090131'
is really interpreted as 12am, or
BETWEEN '20090101 00:00:00' AND '20090131 00:00:00'
so will miss anything that occurred during the day of Jan 31st. In this case, you will have to use:
myDate >= '20090101 00:00:00' AND myDate < '20090201 00:00:00' --CORRECT!
or
BETWEEN '20090101 00:00:00' AND '20090131 23:59:59' --WRONG! (see update!)
UPDATE: It is entirely possible to have records created within that last second of the day, with a datetime as late as 20090101 23:59:59.997!!
For this reason, the BETWEEN (firstday) AND (lastday 23:59:59) approach is not recommended.
Use the myDate >= (firstday) AND myDate < (Lastday+1) approach instead.
Good article on this issue here.
Real world example from SQL Server 2008.
Source data:
ID Start
1 2010-04-30 00:00:01.000
2 2010-04-02 00:00:00.000
3 2010-05-01 00:00:00.000
4 2010-07-31 00:00:00.000
Query:
SELECT
*
FROM
tbl
WHERE
Start BETWEEN '2010-04-01 00:00:00' AND '2010-05-01 00:00:00'
Results:
ID Start
1 2010-04-30 00:00:01.000
2 2010-04-02 00:00:00.000
if you hit this, and don't really want to try and handle adding a day in code, then let the DB do it..
myDate >= '20090101 00:00:00' AND myDate < DATEADD(day,1,'20090101 00:00:00')
If you do include the time portion: make sure it references midnight. Otherwise you can simply omit the time:
myDate >= '20090101' AND myDate < DATEADD(day,1,'20090101')
and not worry about it.
BETWEEN (Transact-SQL)
Specifies a(n) (inclusive) range to test.
test_expression [ NOT ] BETWEEN begin_expression AND end_expression
Arguments
test_expression
Is the expression to test for in the range defined by begin_expression
and end_expression. test_expression
must be the same data type as both
begin_expression and end_expression.
NOT
Specifies that the result of the predicate be negated.
begin_expression
Is any valid expression. begin_expression must be the same data
type as both test_expression and
end_expression.
end_expression
Is any valid expression. end_expression must be the same data
type as both test_expression and
begin_expression.
AND
Acts as a placeholder that indicates test_expression should be
within the range indicated by
begin_expression and end_expression.
Remarks
To specify an exclusive range, use the
greater than (>) and less than
operators (<). If any input to the
BETWEEN or NOT BETWEEN predicate is
NULL, the result is UNKNOWN.
Result Value
BETWEEN returns TRUE if the value of
test_expression is greater than or
equal to the value of begin_expression
and less than or equal to the value of
end_expression.
NOT BETWEEN returns TRUE if the value
of test_expression is less than the
value of begin_expression or greater
than the value of end_expression.
If the column data type is datetime then you can do this following to eliminate time from datetime and compare between date range only.
where cast(getdate() as date) between cast(loginTime as date) and cast(logoutTime as date)
It does includes boundaries.
declare #startDate date = cast('15-NOV-2016' as date)
declare #endDate date = cast('30-NOV-2016' as date)
create table #test (c1 date)
insert into #test values(cast('15-NOV-2016' as date))
insert into #test values(cast('20-NOV-2016' as date))
insert into #test values(cast('30-NOV-2016' as date))
select * from #test where c1 between #startDate and #endDate
drop table #test
RESULT c1
2016-11-15
2016-11-20
2016-11-30
declare #r1 int = 10
declare #r2 int = 15
create table #test1 (c1 int)
insert into #test1 values(10)
insert into #test1 values(15)
insert into #test1 values(11)
select * from #test1 where c1 between #r1 and #r2
drop table #test1
RESULT c1
10
11
15
I've always used this:
WHERE myDate BETWEEN startDate AND (endDate+1)