SQL Server: Using between with getdate - sql

What does the below query mean?
CONVERT(date,GETDATE()-1) between d.baslangictarihi and d.bitistarihi
I know how to use between by first selecting the column name and then giving the value. but here it is given the value first and then called 2 columns.

As others explained, this somewhat quirky condition checks whether yesterday's date CONVERT(date,GETDATE()-1) is between two date fields baslangictarihi and bitistarihi. More importantly, it does so without preventing the server from using any indexes that cover baslangictarihi and bitistarihi.
Indexes are created based on the actual stored values, so applying a function to a field prevents the server from using indexes to speed up searching.
So while baslangictarihi <= GETDATE() can use any indexes that cover that field to limit processing only to the matching table rows, dateadd(d,1,baslangictarihi) <= GETDATE() would have to process all table rows, calculate the result and compare it against GETDATE(). In a large table, this can be very slow.
SQL Server Date quirks
The first part has some quirks too, due to SQL Server's somewhat quirky date support. To be fair all databases and programming languages have quirks when it comes to dates.
GETDATE() returns the legacy datetime type which often behaves as a float, with the integral part an offset from 1899-12-30 (no typo, it really is December 30), and the fractional representing time. That's how dates were stored in Visual Basic in the 1990s and Excel (OADate format)
Since GETDATE() acts as a float, it's possible to subtract days by subtracting integers, so GETDATE()-1 is equivalent to DATEADD('d',GETDATE(),-1).
SQL Server has no interval type, so in some quirky code you'll even see people storing intervals as datetime, eg 0000-00-01 01:00 and adding two dates directly. None of the "new" date types introduced in ... 2005 (datetime2,datetimeoffset,date) allows this.
Finally, convert(date,....) converts datetime to date, a type that only contains a date. Effectively, this truncates the time part returned by GETDATE()
The same expression without quirks would be CONVERT(date,DATEADD(d,-1,GETDATE()))

Well you could "explode" the BETWEEN expression, such that this:
CONVERT(date, GETDATE() - 1) BETWEEN d.baslangictarihi AND d.bitistarihi
becomes this:
CONVERT(date, GETDATE() - 1) >= d.baslangictarihi AND
CONVERT(date, GETDATE() - 1) <= d.bitistarihi
This is just checking if yesterday's date happens to be in between baslangictarihi and bitistarihi, both ends included.

lets consider a sample data to understand this better.
membership_dim
id
name
dob
membership_start_date
membership_end_date
1
abc
19-05-1976
01-05-2020
31-12-2022
2
efg
10-01-1990
21-01-2018
31-12-2021
3
xyz
31-01-1990
12-01-2022
31-12-2022
Your Query
CONVERT(date,GETDATE()-1) between d.baslangictarihi and d.bitistarihi
rewriting to match the above sample data
select * from membership_dim where CONVERT(date,GETDATE()-1) between membership_start_date and membership_end_date
Result set
id
name
dob
membership_start_date
membership_end_date
1
abc
19-05-1976
01-05-2020
31-12-2022
3
xyz
31-01-1990
12-01-2022
31-12-2022
Explanation:
lets breakdown the code
CONVERT(date,GETDATE()-1)
-> getdate()-1 = returns yesterday's date in datetime format (01-23-2022 xx:xx:xx.xxx)
-> convert = converts the datatime to date (01-23-2022)
-> between = a comparison operator
01-23-2022 between 01-05-2020 and 31-12-2022 - returns true
01-23-2022 between 21-01-2018 and 31-12-2021 - returns false
01-23-2022 between 21-01-2018 and 31-12-2022 - returns true

Just understand that everything in a predicate like this is an expression. CONVERT(date,GETDATE()-1) means yesterday without the time component. Those two columns are whatever values are on the row that's being considered at the time. You know what it means if there's a column on the left, but this is no different. It gets evaluated just the same.

Related

LEFT Function in SQL Server returns no results

I am trying to place data corresponding to a certain month into a temp table from an SQL database.
DROP TABLE
#ComPAIR_Alliance_Table
SELECT
IMOno
,period
,[service]
,alliances
INTO
#ComPAIR_Alliance_Table
FROM
com_COMPAIR.dbo.Data_BlueWaterCapacity_US_2
WHERE
LEFT(period, 7) = '2015-03'
SELECT
*
FROM #ComPAIR_Alliance_Table
The period field is in the following format: 2015-03-29 00:00:00.000
However, my code just returns an empty temp table with the right column names but no rows (even though I know for sure rows with that date exist in my table). I have already made sure that the period column is indeed a string by using is.numeric.
Could someone please help me out with what the problem could be?
Thanks!
If it is a date/datetime/datetime2 then you can compare it with 2015-03 like:
WHERE period >= '2015-03-01'
AND preiod < DATEADD(MONTH, 1, '2015-03-01')
In case there is confusion:
The above will match all March 2015 dates such as 2015-03-31, 2015-03-31 23:59:59 and 2015-03-31 23:59:59.9999999
The above is sargable: the DATEADD part does not depend on the table rows
Guessing Period is a date. If it is, stop treating it like a varchar, it isn't one. If you want values from March 2015 then do:
WHERE period >= '20150301'
AND period < '20150401'
LEFT is doing some weird stuff, because LEFT causes an implicit cast to String from Date. You can see this question for more information, but you're getting exactly what you told SQL to get - a join on rows where the left 7 characters of period equal '2015-03' which will not happen, since you're liking comparing against something like 'Jan 01'
The LEFT function needs to implicitly convert your datetime column to a varchar value to do it's work. SQL Server is choosing the varchar format of the date based on it's internationalization settings. On my server, its Mar 29 2015 12:00AM, and LEFT yields Mar 29. That's why it's not equal to 2015-03.
You should treat your column as a datetime and then perform the comparison using a valid datetime comparison, like this :
WHERE period BETWEEN '1/1/2015' AND '1/31/2015'
the date is stored as a date type. You may want to try
where convert(varchar(20), period,121)
which would convert it to string...

How would SQL treat this date format?

I had a coworker run a data pull for me. The query was essentially
SELECT a, b, c
FROM table
WHERE date >= 06/01/2018
The where clause being June 1, 2018. The query ran but the date filter was incorrect (not ‘2018-06-01’) How did the server interpret the date used? Was any filtering applied?
If you specified exactly as you have shown it, without quotes, then it would probably have:
1) Calculated 6 divided by 1 divided by 2018 (resulting in an integer zero)
2) Converted the dates in your database to an int to match the compare data type, and done a compare.
I expect this returned all your rows.
You can use this (datediff) function.
SELECT a, b, c
FROM table
WHERE datediff(dateVar, from_unixtime(unix_timestamp('2018/06/01','yyyy/MM/dd'),'yyyy-MM-dd')) >= 0
from_unixtime(,'yyyy-MM-dd') converts string to a string of given format, e.g. '2018-06-01'
Alternatively, these are functions which can help:
date_sub(,xxx) subtracts xxx days from the string, and returns a new
string in the same format.
unix_timestamp(string date,string pattern) converts a
string of a given pattern to unix time stamp, ) if fail.
Reference: How to change date format in hive?
This condition is:
WHERE date >= 06/01/2018
The last part is a numerical expression which I believe is interpreted as (06 / 01) / 2018. Depending on the database, this would either be 0 or about 0.00297, depending on whether your database does integer division.
Now the database has a bit of a conundrum. It has a date on one side and a number on the other. The rules of type conversion say to convert the date to a number. Depending on the database, this could be an error or a valid number -- which would be larger than 0.
The correct way to express this is:
WHERE date >= '2018-06-01'
or:
WHERE date >= DATE '2018-06-01'

What is being compared? GETDATE() - TSQL

Hello I am wondering what gets compared or what the representation of the
GETDATE() > 1
is in the following line of T-SQL code below.
WHERE DATEDIFF(dd, CDF_AS_OFDATE, GETDATE()) > 1 )
What would happen if I decided to use 100 instead of 1? (I tried it, simply returned a smaller result set).
It's comparing the difference in days between CDF_AS_OFDATE and the current date, to see if it's more than 1 day. If you change it to those that have more than 100 days difference, it would most likely be a much smaller result set.
(You can determine it's in days by noticing that it's using DATEDIFF() with the dd parameter, which indicates you want the difference in days.)
it check if there was more than 1 day difference between the two date (then vs now)
SQL Server DATEDIFF() Function
The DATEDIFF() function returns the time between two dates.
Syntax
DATEDIFF(datepart,startdate,enddate)
Where startdate and enddate are valid date expressions and datepart can be one of the following:
day dd, d
Example
Now we want to get the number of days between two dates.
We use the following SELECT statement:
SELECT DATEDIFF(day,'2008-06-05','2008-08-05') AS DiffDate
Result:
DiffDate
61
The answer is in the DATEDIFF part of the WHERE clause.
It actually evaluates only those rows where the value of CDF_AS_OFDATE at least 1 day different from the current system date.
Where to start...
In your first example...
where getdate() > 1
First getdate() returns the current date and time-of-day as a datetime value. If you read the documentation, you'll discover that (1) there is no implicit conversion from datetime to int, but there is an implicit conversion from int to datetime. That means the expression is pretty much identical to
where getdate() > convert(datetime,1)
The epoch (zero point) of the SQL Server calendar is 1900-01-01 00:00:00.000, which is what you get if you say convert(datetime,0) or convert(datetime,''). When you convert an int value to a datetime value, the integer value is taken to indicate an offset in days since the epoch. The conversion is performed by adding that many days to the epoch to get the resulting datetime value: convert(datetime,1) thus yields the datetime value 1900-01-02 00:00:00.000 and your expression is thus the equivalent of
where getdate() > '1900-01-02 00:00:00.000'
which expression will always be true unless you've seriously mucked with your systems clock.
In your second example...
where datediff( dd , CDF_AS_OF_DATE , getdate() ) > 1
getdate() as noted earlier gives you the current date and time-of-day.
datediff() returns the delta between two datetime values in the requested units of time. If you want to be pedantic about things (and I do), depending on the unit requested, the resulting value is not necessarily correct (depend on your definition of "correct"): what you get is the count of unit boundaries between the two datetime values. So even though exactly one second separates the two datetime values in the expression below,
datediff(dd,'Dec 31, 2013 23:59:59','Jan 1, 2014 00:00:00') returns 1 indicating a delta of 1 day, whilst
datediff(year,'Dec 31, 2013 23:59:59','Jan 1, 2014, 00:00:00') likewise returns1` indicating a delta of 1 year.
So your where clause is restricting the result set to rows where the delta (in days) from the as-of date to the current date/time is greater than 1.

Comparing equality of date and datetime in SQL Server

I am returning rows based on a date field equaling a datetime field. They obviously only directly match when in the format of dd/MM/yyyy = dd/MM/yyyy 00:00:00 but I am looking to disregard the time.
There are 3 methods which I have tried, they all work, but I am wondering what is best.
1 - CONVERT(varchar(10),MyDate,103) = CONVERT(varchar(10),MyDateTime,103))
2 - MyDate = CONVERT(date,MyDateTime)
3 - MyDate = CAST(MyDateTime AS date)
4 - MyDate = DATEADD(dd, DATEDIFF(dd, 0, MyDateTime), 0)
To me, #1 should be the slowest, converting to string then using string comparison surely should be least efficient. But in tests it is the fastest! Below is my tests:
1 - 303ms average
2 - 284ms average
3 - 273ms average
4 - 1745ms average
Test is from a sample size of ~300,000
Is there a reason for this? Is the first option genuinely the best option?
EDIT: Changed the test values to reflect the tests being ran 10 times each for 300k records. Changes the outcome to show all are pretty similar apart from the DATEADD/DATEDIFF method Tim Schmelter mentioned below. That seems to be by far the least efficient.
I would say that #3 is the best choice. Here are my reasons.
You have already performed the performance work, so I won't redo it. Your updated numbers show options 1-3 to be very similar so we can put performance aside, except to rule out #4.
Once performance is settled, it's on to best practices and readability. #1 is definitely to most code and the hardest to read so I would rule that out. This same reason applies to the, already ruled out, #4.
This leaves us with #2 and #3. My selection goes to #3 because CAST is part of the SQL standard and is more portable than CONVERT. So, I would recommend always using CAST, whenever you do not need the special features of CONVERT.
If MyDate is a parameter then there is a fifth option:
Check if MyDateTime lies between [MyDate, MyDate + 1 DAY). If there is an index on that column then this query can use index seek instead of index scan.
DECLARE #MyDate1 AS DATETIME = '2015-01-01' -- 2015-01-01 00:00:00
DECLARE #MyDate2 AS DATETIME = DATEADD(DAY, 1, #MyDate1) -- 2015-01-02 00:00:00
SELECT ... WHERE MyDateTime >= #MyDate1 AND MyDateTime < #MyDate2

Convert SQL server datetime fields to compare date parts only, with indexed lookups

I've been doing a convert(varchar,datefield,112) on each date field that I'm using in 'between' queries in SQL server to ensure that I'm only accounting for dates and not missing any based on the time part of datetime fields.
Now, I'm hearing that the converts aren't indexable and that there are better methods, in SQL Server 2005, to compare the date part of datetimes in a query to determine if dates fall in a range.
What is the optimal, indexable, method of doing something like this:
select * from appointments
where appointmentDate>='08-01-2008' and appointmentDate<'08-15-2008'
The best way to strip the time portion of a datetime field is using datediff and dateadd functions.
DateAdd(day, datediff(day,0, MydateValue), 0)
This takes advantedge of the fact that SQL Server stores dates as two integers, one representing the number of days since day "0" - (1 jan 1900), and the second one which represents the number of ticks (each tick is about 3.33 ms) since midnight (for the time) *.
the formula above simply has to only read the first integer. There is no conversion or processing required, so it is extremely fast.
To make your queries use an index... use this formula on the input filtering parameters first, or on the "other" side of the equal sign from the tables date time field, so that the query optimizer does not have to run the calculation on every datetime field in the table to determine which rows satisfy the filter predicate. This makes your search argument "SARG-able" (Search ARGument)
Where MyDateTimeColumn > DateAdd(day,
datediff(day,0, #MydateParameter), 0) -- SARG-able
rather than
Where DateAdd(day, datediff(day,0,
MyDateTimeColumn ), 0) > #MydateParameter -- Not SARG-able
* NOTE. Internally, the second integer (the time part) stores ticks. In a day there are 24 x 60 X 60 X 300 = 25,920,000 ticks (serendipitously just below the max value a 32 bit integer can hold). However, you do not need to worry about this when arithmetically modifying a datetime... When adding or subtracting values from datetimes you can treat the value as a fraction as though it was exactly equal to the fractional portion of a day, as though the complete datetime value was a floating point number consisting of an integer portion representing the date and the fractional portion representing the time). i.e.,
`Declare #Dt DateTime Set #Dt = getdate()
Set #Dt = #Dt + 1.0/24 -- Adds one hour
Select #Dt
Set #Dt = #Dt - .25 -- Moves back 6 hours
Select #Dt`
Converting numeric types to string values (a type of Boxing) is not the best performing method of doing what you are looking for. Its not really about index-able, because the actual column type is date time.
If you are looking for the best way query for dates, then your example is right, but you may want to take into account the 3 ms precision difference in MSSQL. It can mean that records from one day can show up in another day's result.
This
select * from appointments where appointmentDate>='08-01-2008' and appointmentDate<'08-15-2008'
Should be this
select * from appointments where appointmentDate>='08-01-2008' and appointmentDate<='08-14-2008 23:59:59.996'
It's correct - doing the conversion will execute the conversion for every row queried. It's better to leave the date columns as dates, and pass in your where clauses as dates:
select * from appointments where appointmentdate between
'08/01/2008' AND '08/16/2008'
Note: Leaving off the time means midnight (00:00.000), so you will include all times for 08/01, and all times from 08/15, and anything that is exactly 08/16/2008 00:00:00
Have a computed persisted column calculate the expression you need. If columns are computed and persisted, they can also be indexed.
There is also the way described at http://www.stillnetstudios.com/comparing-dates-without-times-in-sql-server/
SELECT CAST(FLOOR(CAST( getdate() AS float )) AS datetime)