SQL - comparing date parameter to datetime - sql

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.

Related

SQL, casting a string to date so I can use GETDATE()

I am using SQL Server Management Studio 18 against SQL Server 2016. I have some material that are in batches and those batches have expiration dates that are held as strings, but are basically in the format of 'yearmonthday', e.g. '20210312' for March 3rd, 2021. My goal is to only see material that is expiring after the current date. Whenever I try to CAST the expiration date column AS DATE within the WHERE clause, I get this error:
Conversion failed when converting date and/or time from character string
(or something similar when trying different methods).
So, right now my code looks like this:
SELECT MaterialColumn, BatchColumn, CAST(ExpirationColumn AS DATE)
FROM StockTable
WHERE CAST(ExpirationColumn AS DATE) > CAST(GETDATE() AS DATE)
If I don't do the WHERE clause, I know I can CAST the ExpirationColumn as DATE without issue, but when it's in the WHERE clause, I run into that error. Is there any way I can filter to see only the dates that I want?
You can use try_cast() instead:
SELECT MaterialColumn, BatchColumn, CAST(ExpirationColumn AS DATE)
FROM StockTable
WHERE TRY_CAST(ExpirationColumn AS DATE) > CAST(GETDATE() AS DATE);
You can also find the bad values:
SELECT ExpirationColumn
FROM StockTable
WHERE TRY_CAST(ExpirationColumn AS DATE) IS NULL AND ExpirationColumn IS NOT NULL;
It sounds like you might need to fix some data values.
Honestly, if your dates are all stored in the format yyyyMMdd then there's no need to convert. Instead use a varchar parameter, as (at least) a varchar in the format yyyyMMdd had the same sort order as a date.
As a result you just convert GETDATE to the right format:
WHERE Expiration > CONVERT(varchar(8), GETDATE(), 112)
Of course, this doesn't change my statements in the comment; fix your design, don't stores dates as a string but as a date (and time) data type.

How do I convert a value after using DATEADD with it

I have a little query that strips the date from the datetime field but when I try to convert it from GMT to CST it readds the date. Is there a better way to do this?
Location table:
arrival
4-6-2018 12:35:43
SELECT arrival
FROM(
SELECT CONVERT(VARCHAR(8), arrival))
FROM locations
)a
This query will give me this result:
12:35:43
SELECT (DATEADD(hour,-5,arrival))
FROM(
SELECT CONVERT(VARCHAR(8), arrival))
FROM locations
)a
4-6-2018 12:35:43
This query will give readd the date. How can I remove the date and then do the dateadd function without it readding the date
arrival seems to be a DateTime, which always carries a date part. You need a time instead, supported by SQL Server 2008+:
cast(DATEADD(hour,-5,arrival) as time)
To quote from DATEADD (Transact-SQL) - Return Types:
The return data type is the data type of the date argument, except for string literals. The return data type for a string literal is datetime. An error will be raised if the string literal seconds scale is more than three positions (.nnn) or contains the time zone offset part.
Emphasis my own.
As you are passing a string (varchar), then DATEADD is returning a datetime.
Like those in the comments have said, if you use the correct data type (time) this problem goes away:
SELECT DATEADD(HOUR, -5,CONVERT(time,Arrival))
FROM (VALUES('4-6-2018 12:35:43'),('4-6-2018 07:35:43'),('4-6-2018 03:35:43')) V(Arrival)
Probably this is what you are asking for:
SELECT Convert(Varchar(8), DATEADD(hour,-5,arrival), 108)
FROM locations;
Note: This is compatible with SQL server versions that doesn't have Time datatype too.

Getting specific date values in 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.

Storing the date Only in SQL Server 2005

How do I avoid storing the time portion of a datetime in SQL Server, i.e. if I have a value of 2011-01-01 00:00:00.000 I want to store only 2011-01-01?
I want to ensure that only the date portion is stored.
The DateTime data type ALWAYS stores the date AND time. So you are left with using CONVERT/CAST to obtain a particular format, or use the YEAR(), MONTH() or DAY() methods to isolate date details depending on your need.
SQL Server Date Formats.
The easiest solution is just to not expose the time portion to the user. However, if you really need to make sure only the date part is stored, you could force the time portion to midnight/midday/any constant time before storing the value.
The built-in DATETIME data type stores both the date and time data. If you specify only the date portion then the time will be 12:00:00 or something like that.
Funny story: I saw a database once where there was a date and a time field, both stored the date and the time, but each was used only for half of the data. Some people do silly things :)
If you cast a DateTime to an Int and back you will get a DateTime with 00:00 as the time part.
So you could save all your dates as integers in the database.
Either add a computed column:
dateonly AS CONVERT(DATETIME, CONVERT(CHAR(8), date_with_time, 112), 112)
or truncate your date right on insert:
INSERT
INTO mytable (dateonly)
VALUES CONVERT(DATETIME, CONVERT(CHAR(8), GETDATE(), 112), 112)
, making a CHECK on your dateonly column to raise an error when someone tries to insert a non-truncated value:
CHECK (dateonly = CONVERT(DATETIME, CONVERT(CHAR(8), date_with_time, 112), 112))
Just represent the date as a yyyMMdd integer value.

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)