How is SQL Server evaluating this query with a smalldatetime value? - sql-server

Consider the following demonstration queries and the results (note the only difference in the two queries is the comparison operator in the WHERE clause):
The LUpd_DateTime column is a smalldatetime data type. Since the smalldatetime data type doesn't actually contain any seconds (rounding occurs up or down to the nearest minute), the only explanation I have for the two queries below is that SQL Server is converting the date string to a smalldatetime type and rounding up to the nearest minute, thus changing the date string to '9/20/2018 00:00:00 AM'.
Can anyone confirm this?

SQL Server is converting the date string to a smalldatetime type and
rounding up to the nearest minute, thus changing the date string to
'9/20/2018 00:00:00 AM'. Can anyone confirm this?
Yes. To compare two expressions SQL Server always converts both expressions to a common data type. Whichever expression's data type has the lower Data Type Precedence is converted. The "date string" is an expression of type varchar which has a lower precedence than smalldatetime. So the string is converted to smalldatetime for comparison. And you can verify that the conversion rounds to the nearest value:
select cast( '2018-09-19 11:59:59' as smalldatetime)
outputs
2018-09-19 12:00:00

I think you may have mistyped your explanation? You state that the column is a smalldatetime but then go on to say you think the query is converting the "date string to a smalldatetime". If what I said is correct, then a simple check of logic will show your assumption to be true. Yes, when converting it will become "09/20/2018 00:00:00 AM".
DECLARE #dateField AS date
SET #dateField = '2018-09-20 06:23:00'
SELECT CONVERT(smalldatetime, #dateField)

It is covered clearly in the documentation.
Defines a date that is combined with a time of day. The time is based
on a 24-hour day, with seconds always zero (:00) and without
fractional seconds.
If you look at your results all are 00 seconds.

Related

SQL Server query that returns data between two date times with format 01/07/2020 01:01:01 a. m

I've been having problems with a query that returns data between two date times, the query that I'm trying to fix is this one
pay.date BETWEEN '01/06/2020 00:28:46 a. m.' AND '01/06/2020 10:38:45 a. m.'
That query does not detect the a. m. part and if I have a payment at 10 am and 10 pm it will detect both payments as the t. t. part is not detected, I've been searching for a while now with no luck, thanks in advance :)
Do the filtering by an actual datetime type:
cast(replace(replace(pay.date, ' a. m.', 'am'), ' p. m.', 'pm') as datetime)
It might be better to use convert() so you can specify the proper format. If you can't supply the date literals in a readily convertible format then do a similar replace and cast on those too.
Use a literal format that is unambiguous and not dependent on runtime or connection settings. More info in Tibor's discussion.
In this case:
where pay.date between '20200601 00:28:46' and '20200601 10:38:45'
Notice that I assume June, not January - adjust as needed. Between is inclusive and be certain that you understand the limitations of the datatype for pay.date. If datetime, the values are accurate to 3ms. Verify that your data is consistent with your assumption about accuracy to seconds.

Why does ISDATE(YEAR(GETDATE())) return 1?

Why does ISDATE(YEAR(GETDATE())) or ISDATE(2020) or ISDATE(<any four digit number>) or ISDATE(<any expression that returns a four digit number>) return a 1?
With all these returning a 1, can we really rely on ISDATE() to know if an expression is a date expression?
Here's why I'm concerned:
SELECT ISDATE(4000-2000) returns a 1 although it's not a date expression.
Because ISDATE (like ISNUEMRIC) isn't a great function. ISDATE was designed with the older date and time data types (datetime and smalldatetime) in mind, which means that a int can be implicitly converted to Date and Time. This is not true for the new Date and Time datatypes (such as datetime2 and date).
For the older data types, an int value represents that many days after 1900-01-01. So 0 is 1900-01-01, and 2 would be 1900-01-03. 2020 would therefore be 1905-07-14.
If you truly want to see if a value is a valid date and time value, for the data type you are using, use TRY_CONVERT. For example TRY_CONVERT(date,2020) will return NULL, as 2020 cannot be converted. On the other hand TRY_CONVERT(datetime,2020) will return 1905-07-14 as it can be converted; even if the value isn't what you might expect.
Additional Note: 4 digit strings can also be implicitly converted. a 4 digit string would represent the year of the date only, and therefore '2000' would represent 2000-01-01. This is true for both the older date and time data types, and the new ones. Both TRY_CONVERT(date,'2020') and TRY_CONVERT(datetime,'2020') will return 2020-01-01.
As Martin Smith mentions, the above is note actually more relevant to what is happening here. As per ISDATE (Transact-SQL) the parameter for ISDATE is:
Is a character string or expression that can be converted to a character string. The expression must be less than 4,000 characters. Date and time data types, except datetime and smalldatetime, are not allowed as the argument for ISDATE.
This means that the expression ISDATE(2020) and ISDATE('2020') are synonyms. This is actually really bad, as 2020 cannot be converted to a date (implicitly or explicitly), but can be to a datetime . This further (in my opinion) cements that ISDATE should be avoided. Considering that TRY_CONVERT is available in all supported versions of SQL Server (including those in extended support) it is by far a much safer option.
The date range for DateTime is 1753-01-01 through 9999-12-31. The year is a valid datepart. It does not have to be a string if it is the year only. SELECT ISDATE('1753') will return 1 whereas SELECT ISDATE('1752') will return 0. See the full definition here.
Because any four digit number is a valid year, therefore it is a date, and the ISDATE() function checks an expression and returns 1 if it is a valid date, otherwise 0.
At 4000-2000 it only checks the return value (2000), which is still valid. If you would give it as a string by parameter ("4000-2000"), it would fail.

EPOCH timestamp from SQL Server

I feel like I've read a ton of these corresponding posts such as,
converting Epoch timestamp to sql server(human readable format)
& How do I convert a SQL server timestamp to an epoch timestamp?
But can't seem to get my particular use-case working. I need to convert an epoch timestamp to a normal date/time value. Currently, the column is a nvarchar(max) type. Here's an example of one of the dates:
1478563200000
I'm trying to get it to look like the following:
2019-01-14 00:00:00.0000000
I've tried the following to no success all with the same error message:
select DATEADD(SS, CONVERT(BIGINT, baddate), '19700101') as gooddate
from table
"Arithmatic overflow error converting expression to data type int"
I've tried minutes, seconds, days, all same error message and at this point I'm about to tell the guys to send the data in a different format.
Try
select DATEADD(SS, CONVERT(INT, CONVERT(BIGINT, baddate)/1000), '19700101') as gooddate
from table
DATEADD expects an int, not a bigint. Since your timestamp is in milliseconds, it won't "fit" in an int. If you trade-in millisecond resolution for second resolution by dividing by 1000 it will fit in an integer and make DATEADD happy. So first we convert the NVARCHAR to BIGINT (why store as NVARCHAR in the first place?), then divide by 1000 and then convert to INT.
Another option is to divide the value by 1000 at the time of insert (and, again, make the column an int in the first place). That'll save a lot of CONVERTs everywhere (you can get rid of them all) and probably speed up your queries quite nicely. Then again, you could even convert the column to datetime (or datetime2 or whatever type is best suited) and leave out the entire dateadd/convert mess in your queries alltogether. Always try to get your data ad close to the final datatype you need it in later.
Edit: I just realized you can probably leave one convert out:
select DATEADD(SS, CONVERT(BIGINT, baddate)/1000, '19700101') as gooddate
from table
This is the same as the original suggestion, only this time the cast to int is implicit. But converting your data upon insert is still probably the better idea. So the rest of my post still stands.
You can get the correct result to the millisecond, that works with years 0001 through 9999, using the accepted answer
here.
declare #x nvarchar(max) = N'1478563200000'
select dbo.UnixTimeToDateTime2(#x)

DT_DBTIMESTAMP2 having only 3 digits

I'm having (DT_DBTIMESTAMP2,7)GETDATE() in SSIS Derived Column Transformation and Table column with datetime2(7).
Even though I set 7 Digit Second Scale in both, but seems it comes only 3 digit.
For example, I expected like '2018-05-02 16:45:15.6192346' but it comes '2018-05-02 16:45:15.6190000'.
The reason why I need the millseconds, I'd like to sort out the latest record from any duplications using timestamp. I realized only 3 digit second scale is not enough for this pourpose.
Except for Derived Column Transformation and Table Columns, is there any requrired setting in SSIS package? Any advices would be appreciated.
GETDATE() returns a datetime, you should use SYSDATETIME() instead. See documentation.
edit
As noted by Larnu, you are probably using SSIS expression GETDATE, rather that the sql expression GETDATE as I assumed. The point is more-or-less the same though. GETDATE returns a DT_DBTIMESTAMP, where "The fractional seconds have a maximum scale of 3 digits." (Source).
Although this is almost the same as what HoneyBadger has said, I'm expanding a little, as the OP isn't using the GETDATE() expression in SQL Server. The value 2018-05-02 16:45:15.619 could never be returned by GETDATE() (Transact-SQL) as it's only accurate to 1/300th of a second (thus the final digit can only every be 0,3, and 7 (technically 0, 333333333~ and 666666666~, which is why the final digit is a 7, as it's rounded up)).
In SSIS the GETDATE() expression returns a datatype of DB_TIMESTAMP. According to the Documentation:
A timestamp structure that consists of year, month, day, hour, minute,
second, and fractional seconds. The fractional seconds have a maximum
scale of 3 digits.
Thus, the last 4 characters are lost. Unfortunately, I don't believe there is a function in SSIS that returns the the current date and time to the accuracy you require. Thus, if you need this high level, you'll likely need to use an expression in SQL Server that does, such as SYSDATETIME() that HoneyBadger recommended.

SQL Server Convert datetime to mm/dd/yyyy format but still be a datetime datatype

I would like to keep my dates as datetime datatype by also be in MM/DD/YYYY format. I know how to do this by converting them to a varchar, but want to keep the datetime format. Can anyone help with this?
Currently I have tried
SELECT CONVERT(datetime, GETDATE(), 101)
which is not working...
There is a basic misunderstanding in your question. Repeat after me: Datetimes don't have a format.
It helps if you think of them as just an array of seven integers (year, month, day, hours, minutes, seconds, milliseconds) with certain constraints. That's not in any way accurate, but it helps to get the notion out of your head that something akin to 12/31/2015 is stored in your database.
Datetimes only get a format when (implicitly or explicitly) being converted to strings. You already know how to set the format when explicitly converting to string, now all that is left to do is to find the implicit conversion that is obviously bothering you and replace it with an explicit one.
Date and datetime Values stored in the database are NOT in any recognizable format. They are stored in binary (1s and 0s) in a proprietary format where one part represents the number of days since a defined reference date (1 jan 1900) in SQL server). and the other part represents the time portion of the value. (in sql server, its the number of 1/300ths of a second since midnight.)
ALL formatting of dates and date times, no matter what format you wish for, is done only after the values have been extracted from the database, before you see them on screen, in whatever application you are using.
You can find all the formats that the SQL Server convert function can use on this MSDN Convert Link

Resources