Getting specific date values in SQL - sql

I need to perform a query in which a require date value to be current date(or specific date) - 7 days. I came across many solutions like these,
(SELECT CONVERT(VARCHAR(10),DATEADD(DD, DATEDIFF(DD, 0, GETDATE()), -7),120))
However, for getting a week's value, i found a similar solution from here.
SELECT CONVERT(varchar(25),PostDate,107) AS duration, count(*) AS posts
FROM MDBDetails
WHERE DATEDIFF(week, PostDate,GETDATE()) = 1
GROUP BY CONVERT(varchar(25),PostDate,107)
ORDER BY duration
But, a simple query like GETDATE() -7 or the below code seems to do the required job for me.
DECLARE #Date DATETIME = '12/25/13'
SELECT #Date-7
My question is that, isn't the above method of subtracting date with a numerical value a good practice?
Of course I get the use of DATEDIFF can be extended to specify months and years too. But, is the above method a good practice? Is it necessary to use only DATEDIFF method to get the exact date values? Are there any major drawbacks or differences when compared to the first two methods? (except for subtracting months and years).

You'd better use DATEADD function.
Because if one day you decide to change date type of your column from DATETIME or SMALLDATETIME to DATE there will be an error.
You cannot subtract days from DATE. You should only use DATEADD function instead.

Related

Using DATEADD instead of less than or equal to when testing against parameters

I'm newer to SQL and I'm playing around with some existing queries in my database in SSMS - something I've been coming across lately is this line:
WHERE DE.Modified >=#FromDate
AND DE.Modified < DATEADD(DAY,1,#ToDate)
Where FromDate and ToDate are given parameters. What I'm wondering is why one might write the second line instead of:
AND DE.Modified <=#ToDate
Is it a best practice in SQL to only use the less than operator and test against a date + 1, or are these the exact same?
EDIT:
FromDate and ToDate are declared as DATETIME:
DECLARE #FromDate DATETIME
, #ToDate DATETIME
You would write this to handle any time component on Modified.
In your version, anything that happens during the day of #ToDate would be missed.
The two are different:
DE.Modified <=#ToDate
checks if the Modified date is less than the ToDate, while
DE.Modified < DATEADD(DAY,1,#ToDate)
checks if the Modified date is less than the day after the ToDate. That is, the ToDate plus one day.
Just a tip when dealing with dates, usually try to compare
WHERE DATEDIFF('d',DE.Modified,#FromDate) >= 0
AND DATEDIFF('d',DE.Modified,#ToDate) <= 0
It won't mater much here but its a good habit because when you will be working on procedures or queries where you can build a calculated column, that column can then be indexed. And it will make your applications that much faster.
Also, you clearly know what you are comparing (days, hours, etc) as Gordon said above.

Casting Strings into Birthdates (that aren't in the future)

I am casting mm/dd/yy strings into dates in redshift using CAST AS DATE CAST(birth_str AS DATE) AS birth_date. The conversion handles the components correctly but the year is being converted into future times whenever it falls below 1970. For example:
birth_str birth_date
07/19/84 1984-07-19
02/07/66 2066-02-07
06/24/84 1984-06-24
01/31/64 2064-01-31
12/08/62 2062-12-08
02/21/36 2036-02-21
02/19/37 2037-02-19
07/01/74 1974-07-01
08/25/50 2050-08-25
08/31/39 2039-08-31
Is there a best practice for getting dates to not fall into the future?
Is there not an argument for this in the cast? (I looked everywhere but I am finding nothing.) Otherwise, I am envisioning the best path forward is testing for the cast date being in the future and then just doing string surgery on the miscreants before recasting them into reasonable dates.
Basically:
if not future date: great.
if future date:
peel out all the date components
slap a 19 onto the yy
glue everything back together
cast into date.
Is this as good as it gets? (I was a bit surprised I could find no one has come up with a better way around this issue already.)
Is there a best practice? Absolutely! Don't store dates as strings. Store dates as date. That is why SQL has native types.
In your case, you could use conditional logic:
select (case when cast(birth_str AS DATE) < current_date
then cast(birth_str AS DATE)
else cast(birth_str AS DATE) - interval '100 year'
end) as birth_date
Or since Redshift can't handle intervals you can go with this:
SELECT (CASE
WHEN birth_str::DATE < CURRENT_DATE
THEN birth_str::DATE
ELSE ADD_MONTHS(birth_str::DATE, -1200)
END) AS birth_date
You can apply a CASE to check the converted DATE IS greater than TODAY or not. If Yes, Just minus 100 years from the results as below.
One Question: Is there any chance of having dates like 02/21/14 which can be belongs to 1900 or 2000?
SELECT
CASE
WHEN CAST('02/21/36' AS DATE) >GETDATE() THEN DATEADD(YY,-100,CAST('02/21/36' AS DATE))
ELSE CAST('02/21/36' AS DATE)
END

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.

SQL - comparing date parameter to datetime

In a SQL Server table, I have a field named Timestamp, which is a 'datetime' field. On a screen I have created, a user will pass in a date (no time), and I need to return all the records for that date.
Just doing Timestamp = #Date doesn't seem to work unless the time in the field is 00:00:00. What is the most efficient way of doing this?
Since you're on SQL Server 2008, you can use the DATE datatype.
Do a comparison between
CAST(Timestamp AS DATE) = #Date
That should work and look just at the date - no time portion.
In general you should think about the data from a specific date as data that falls in a range, not at a single point in time. So ideally your query should use a range, such as:
WHERE [Timestamp] >= #Date
AND [Timestamp] < DATEADD(DAY, 1, #Date)
In this case, luckily, the optimizer is smart and will still use an index if you use CONVERT(DATE, [timestamp]) against the column. However in a lot of cases you need to be careful because this will often make your clause non-sargable.
PS Timestamp is a horrible column name. It's a data type in SQL Server which has nothing to do with date or time.
A common technique for truncating the time part off a datetime value is to use the DATEDIFF and DATEADD functions. In your example it would be used like this to truncate the time part of the Timestamp field.
WHERE #DateEntered = DATEADD(DAY,0, DATEDIFF(DAY, 0, Timestamp))
Bascially it's taking the datetime value and finding the name of days since "the date represented by 0" (for lack of a better description) and then adding that number of days back. This effectively truncates time part.

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)