SQL Server compare datetime fields - sql-server

I have a datetime field in my table that I need to use in a where clause.
The fieldname is DatumAanname
In my dataviewgrid it shows for example "16/12/2014 15:57:04"
I looked at the page from microsoft with all the datetime options for a convert, but this format does not seems to be on that page.
The closest format I can see there is "16 12 2014 15:57:04" which should be 113
I tried a query like this
SELECT o.DatumAanname,
(convert(DATETIME, '16 dec 2014 15:57:04:000', 113)),
(convert(DATETIME, '16 dec 2014 15:57:04', 113)),
(convert(DATETIME, o.DatumAanname, 113))
FROM vwOpdracht o
this returns 4 fields which all look exact the same
but when I do
where (convert(datetime, o.DatumAanname, 113)) = (convert(datetime, '16 dec 2014 15:57:04:000', 113))
it returns 0 records
What am I doing wrong here ?

Dates have no formats, they are native types with their own binary storage. Formats and conversions apply only when converting from/to string and they frequently cause problems if incompatible collations and cultures are used.
The best way to avoid problems is simply to remove strings entirely: use date-typed columns in the table and query using parameterized queries. Actually, if the date is stored as text in the table there is no generic and fault-proof way to acoid problems.
Pass strongly typed values instead of strings (eg DateTime in C#, date, datetime,datetime2 in T-SQL). For example:
where o.DatumAanname= #dateParam
will return matching entries if the values match.
If passing date parameters isn't possible, you should only use the unambiguous ISO8691 format, eg:
where o.DatumAanname = '2014-12-16T15:57:04'
If you want to pass date-only values you can use the unseparated format which is also unambiguous
where o.DatumAanname = '20141216'
One thing to note is that date values must match exactly, down to the millisecond. Typically dates are used in range queries where this isn't an issue. In equality queries though, this can be a problem.
You can avoid this by using datetime2(0) as the field type, which ensures that milliseconds aren't stored at all.
Another option is to use a range instead of an equality query, eg:
where o.DatumAanname between '2014-12-16T15:57:04' and '2014-12-16T15:57:04.999'
or
where o.DatumAanname between '2014-12-16T15:57:00' and '2014-12-16T15:57:59'
if you want minute-level precision

Related

T SQL Conversion failed when converting date and/or time from character string from VARCHAR(MAX)

I'm using SQL Server 2014. I have a date stored as varchar(MAX) in the format of:
2019-02-18
However, I want it in the British format dd/mm/yyyy (103).
This is my SQL:
SELECT CONVERT(DATE, DateField, 103) AS "JobStartDate"
FROM tblTest
However, I keep getting this error:
Conversion failed when converting date and/or time from character string.
What am I missing?
Update: The date is initially stored as varchar max as it is coming from a 3rd party system. I have no control over this and I completly understand this is the wrong format, but this is what I have been given.
I have a date stored as varchar(MAX)
There's your problem right there.
Not only you are using the wrong data type to store dates, you are also using max which is a known performance killer.
The solution to the problem is to alter the table and store dates in a Date data type - but first, you must look up all the objects that depends on that column and make sure they will not break or change them as well.
Assuming this can't be done, or as a temporary workaround, you must first convert the data you have to Date, and then convert it back to a string representation of that date using the 103 style to get dd/mm/yyyy.
Since yyyy-mm-dd string format is not culture dependent with the date data type, you can simply do this:
SELECT CONVERT(char(10), TRY_CAST(DateField As Date), 103) As [JobStartDate]
FROM tblTest
Note I've used try_cast and not cast since the database can't stop you from storing values that can't be converted to dates in that column.
You want to format the DateField column and not convert it to date.
So first convert it to DATE and then apply the format:
SELECT FORMAT(CONVERT(DATE, DateField, 21), 'dd/MM/yyyy') AS JobStartDate
See the demo.

Date conversion in a XML column in SQL Server

How do I convert the below mentioned XML code date format.
<StartDate>2015-12-24T00:00:00</StartDate>
<EndDate>2015-12-29T15:39:20</EndDate>
If you're accessing your XML content with the built-in XQuery functionality, you can just use the .value() method and define the output datatype to be a DATETIME2(3) type - no special treatment necessary:
DECLARE #InputTbl TABLE (ID INT NOT NULL, XmlContent XML)
INSERT INTO #InputTbl (ID, XmlContent)
VALUES (1, '<Root>
<StartDate>2015-12-24T00:00:00</StartDate>
<EndDate>2015-12-29T15:39:20</EndDate>
</Root>');
SELECT
StartDate = XC.value('(StartDate)[1]', 'datetime2(3)'),
EndDate = XC.value('(EndDate)[1]', 'datetime2(3)')
FROM
#InputTbl
CROSS APPLY
XmlContent.nodes('/Root') AS XT(XC)
This returns this output:
There are several correct answers, but I've got the feeling, that these answers don't hit your actual issue:
In my table there is a xml column, and that xml column contains somuch data including date, And i want to update those dates to date format like '2/28/2017' now the date format is like '2012-04-26T00:00:00'
If I got you correctly you want to change the stored dates within your XML to another format, correct?
Simple answer: Don't!
ISO8601 is the standard format for date/time values within XML. The format you would like more 2/28/2017 is culture related and could lead to errors, or even worse!, to wrong values, if day and month both are below 13: 04/05/2017 can be taken as 4th of May or as 5th of April. You should never rely on culture settings!
XML is not meant to be human readable. Or in better words: It is meant to be human readable for technical people only... It is a standardizes string representation of structured, complex documents. The format of values should not bother you! Use an appropriate editor for the presentation.
This might be work
declare #date ='2015-12-24T00:00:00 2015-12-29T15:39:20'
SELECT CONVERT(date, Left(#date,10)) as NewDate
This may help you
DECLARE #X XML ='<StartDate>2015-12-24T00:00:00</StartDate>
<EndDate>2015-12-29T15:39:20</EndDate>'
SELECT #X.value('/StartDate[1]','DATETIME') AS START_DTE
,#X.value('/EndDate[1]','DATETIME') AS END_DTE
+-------------------------+-------------------------+
| START_DTE | END_DTE |
+-------------------------+-------------------------+
| 2015-12-24 00:00:00.000 | 2015-12-29 15:39:20.000 |
+-------------------------+-------------------------+
Update: From comments
The Datatime format in XML follows the ISO8601 standards. And you are thinking to format it native SQL format, which is not correct that you are treating XML like normal text data. The data present in XML format is correct. And If you want you can convert it to native SQL as above mentioned.
There is a good info at Wikipedia ISO 8601(Combined date and time representations)
on how the XML hold date time data.
A single point in time can be represented by concatenating a complete
date expression, the letter T as a delimiter, and a valid time
expression. For example, "2007-04-05T14:30".

SQL Server date formatting issue

I have a SQL Server 2008 database with a transaction table. There is a field defined as NVARCHAR(16). The data stored is date and time formatted like this:
2016100708593100
I need to write a query that looks at that field and pulls data between two dates
select ... from...where convert(varchar,
convert(datetime,left(a.xact_dati,8)),101)
between '9/29/2016' and '10/05/2016'
I have tried other converts but nothing returns any data. If I use >=getdate()-1
I get data so I should be seeing something returned. Any suggestions?
Replace WHERE Clause with
... WHERE CONVERT(DATETIME,LEFT(a.xact_dati,8)) BETWEEN '9/29/2016' AND '10/05/2016'
Assuming YMD order just
where cast(left('2016100708593100', 8) as date) between '20160929' and '20161005'
Note this clause is not satisfied.
The problem is you're trying to do a date comparison with varchar data.
You go half way to converting the initial value to datetime but then back to varchar.
You could rely on Sql to implicitly convert the between parameters to datetime.
select 'matched',convert(datetime,left('2016100708593100',8),112)
where convert(datetime,left('2016100708593100',8),112) between '2016/09/29' and '2016/10/10'
Take the date part of the string and CAST it as DATE and then compare it with the proper date format.
select ... from...
where cast(left('2016100708593100', 8) as date) between '2016-09-29' and '2016-10-10'
a very straight and fast approach could be to keep this in BIGINT
Something like this:
cast(left(a.xact_dati,8) as bigint) between 20160929 and 20161005
But anyway this will not be sargable and therefore cannot use an index.
You might even stick to varchar, as the YYYYMMDD format is safe with alphanumerical sorting. Cutting a string with LEFT is sargable (AFAIK):
left(a.xact_dati,8) between '20160929' and '20161005'

Comparing dates stored as varchar

I need to compare dates that are stored in my database as varchar against today's date.
Specifically, I need to exclude any records with a date that has passed.
I tried:
SELECT * FROM tblServiceUsersSchedule
WHERE ScheduleEndDate !='' AND ScheduleEndDate < '2015/05/31'
This selected values such as 17/06/2012 and 19/04/2015, which have both already passed, along with 01/06/2015 which hasn't.
I then tried to cast the data with:
SELECT *
FROM tblServiceUsersSchedule
WHERE CAST(ScheduleEndDate as DATETIME) < CAST('05/31/2015' as DATETIME) AND ScheduleEndDate !='' AND ScheduleEndDate is not null
But got the following error:
The coversion of a varchar data type to a datetime data type resulted
in an out-of-range value.
I checked the data behind and none are null, none are blank white space. All are dates in the format of dd/mm/yyyy.
I can't figure out how to compare the varchar date stored with todays date.
Storing date values as varchar is simply wrong.
If possible, you should alter the table to store them as date data type.
You can do it in a few simple steps:
Rename the current columns (I'm guessing ScheduleStartDate is also varchar) to columnName_old. This can be easily done by using sp_rename.
Use alter table to add the columns with the appropriate data type.
Copy the values from the old columns to the new columns using an update statement. Since all of the dates are stored in the same format, you can use convert like this: set ScheduleStartDate = convert(date, NULLIF(ltrim(rtrim(ScheduleStartDate_old)), ''), 103) If your sql server version is 2012 or higher, use try_convert. Note i've used the nullif, ltrim and rtrim to convert values that only contains white spaces to null.
Drop and recreate indexes that is referencing these columns. The simplest way to do this is by right-clicking the index on SSMS and choose script index as -> drop and create.
Use alter table to remove the old columns.
Note: if these columns are being referenced in any other objects on the database you will have to change these objects as well. This includes stored procedures, foreign keys etc`.
If you can't change the data types of the columns, and your sql server version is lower then 2012, you need to use convert like this:
SELECT * FROM tblServiceUsersSchedule
WHERE CONVERT(DATE, NULLIF(ScheduleEndDate, RTRIM(LTRIM('')), 103)
< CAST(GETDATE() As Date);
AND ScheduleEndDate IS NOT NULL
Note that if you have even a single row where the column's data is not in dd/MM/yyyy format this will raise an error.
For sql server versions 2012 or higher, use Try_convert. This function will simply return null if the conversion fails:
SELECT * FROM tblServiceUsersSchedule
WHERE TRY_CONVERT(DATE, NULLIF(ScheduleEndDate, RTRIM(LTRIM('')), 103)
< CAST(GETDATE() As Date);
AND ScheduleEndDate IS NOT NULL
Note: I've used CAST(GETDATE() as Date) to remove the time part of the current date. This means that you will only get records where the ScheduleEndDate is at least one day old. If you want to also get the records where the ScheduleEndDate is today, use <= instead of <.
One final thing: Using functions on columns in the where clause will prevent Sql Server to use any indexing on these columns.
This is yet another reason why you should change your columns to the appropriate data type.
Other than what people have already suggested that you should never store DATETIME as VARCHAR. Always store it in a DATETIME type column; I think you should change your condition in WHERE
ScheduleEndDate < '2015/05/31'
To this, in order to get all dates which hasn't passed yet
ScheduleEndDate >= '2015/05/31'
Your query should look like
SELECT * FROM tblServiceUsersSchedule
WHERE ScheduleEndDate IS NOT NULL
AND ScheduleEndDate >= '2015/05/31'
If you HAVE TO store your dates as varchar (and as per other other answers, this is a poor practice), then using an ISO style format like yyyy-mm-dd should allow textual comparisons without issue.
If your column is a date data type, and you're using SQL 2012 or later then use DATEFROMPARTS (or one of its variants) for date comparison, so
WHERE DateToCompare < DATEFROMPARTS (2019, 12, 31)
rather than
WHERE DateToCompare < '2019-12-31'
SQL handles the latter fine, but the former is more "correct".

How to convert SQL Server's timestamp column to datetime format

As SQL Server returns timestamp like 'Nov 14 2011 03:12:12:947PM', is there some easy way to convert string to date format like 'Y-m-d H:i:s'.
So far I use
date('Y-m-d H:i:s',strtotime('Nov 14 2011 03:12:12:947PM'))
SQL Server's TIMESTAMP datatype has nothing to do with a date and time!
It's just a hexadecimal representation of a consecutive 8 byte integer - it's only good for making sure a row hasn't change since it's been read.
You can read off the hexadecimal integer or if you want a BIGINT. As an example:
SELECT CAST (0x0000000017E30D64 AS BIGINT)
The result is
400756068
In newer versions of SQL Server, it's being called RowVersion - since that's really what it is. See the MSDN docs on ROWVERSION:
Is a data type that exposes automatically generated, unique binary numbers within a database. rowversion is generally used as a mechanism
for version-stamping table rows. The
rowversion data type is just an incrementing number and does not
preserve a date or a time. To record a date or time, use a datetime2
data type.
So you cannot convert a SQL Server TIMESTAMP to a date/time - it's just not a date/time.
But if you're saying timestamp but really you mean a DATETIME column - then you can use any of those valid date formats described in the CAST and CONVERT topic in the MSDN help. Those are defined and supported "out of the box" by SQL Server. Anything else is not supported, e.g. you have to do a lot of manual casting and concatenating (not recommended).
The format you're looking for looks a bit like the ODBC canonical (style = 121):
DECLARE #today DATETIME = SYSDATETIME()
SELECT CONVERT(VARCHAR(50), #today, 121)
gives:
2011-11-14 10:29:00.470
SQL Server 2012 will finally have a FORMAT function to do custom formatting......
The simplest way of doing this is:
SELECT id,name,FROM_UNIXTIME(registration_date) FROM `tbl_registration`;
This gives the date column atleast in a readable format.
Further if you want to change te format click here.
Using cast you can get date from a timestamp field:
SELECT CAST(timestamp_field AS DATE) FROM tbl_name
Works fine, except this message:
Implicit conversion from data type varchar to timestamp is not allowed. Use the CONVERT function to run this query
So yes, TIMESTAMP (RowVersion) is NOT a DATE :)
To be honest, I fidddled around quite some time myself to find a way to convert it to a date.
Best way is to convert it to INT and compare. That's what this type is meant to be.
If you want a date - just add a Datetime column and live happily ever after :)
cheers mac
My coworkers helped me with this:
select CONVERT(VARCHAR(10), <tms_column>, 112), count(*)
from table where <tms_column> > '2012-09-10'
group by CONVERT(VARCHAR(10), <tms_column>, 112);
or
select CONVERT(DATE, <tms_column>, 112), count(*)
from table where <tms_column> > '2012-09-10'
group by CONVERT(DATE, <tms_column>, 112);
"You keep using that word. I do not think it means what you think it means."
— Inigo Montoya
The timestamp has absolutely no relationship to time as marc_s originally said.
declare #Test table (
TestId int identity(1,1) primary key clustered
,Ts timestamp
,CurrentDt datetime default getdate()
,Something varchar(max)
)
insert into #Test (Something)
select name from sys.tables
waitfor delay '00:00:10'
insert into #Test (Something)
select name from sys.tables
select * from #Test
Notice in the output that Ts (hex) increments by one for each record, but the actual time has a gap of 10 seconds. If it were related to time then there would be a gap in the timestamp to correspond with the difference in the time.
for me works:
TO_DATE('19700101', 'yyyymmdd') + (TIME / 24 / 60 / 60)
(oracle DB)
Robert Mauro has the correct comment. For those who know the Sybase origins, datetime was really two separate integers, one for date, one for time, so timestamp aka rowversion could just be considered the raw value captured from the server. Much faster.
After impelemtation of conversion to integer
CONVERT(BIGINT, [timestamp]) as Timestamp
I've got the result like
446701117
446701118
446701119
446701120
446701121
446701122
446701123
446701124
446701125
446701126
Yes, this is not a date and time, It's serial numbers
Why not try FROM_UNIXTIME(unix_timestamp, format)?
I had the same problem with timestamp eg:'29-JUL-20 04.46.42.000000000 PM'. I wanted to turn it into 'yyyy-MM-dd' format. The solution that finally works for me is
SELECT TO_CHAR(mytimestamp, 'YYYY-MM-DD') FROM mytable;
I will assume that you've done a data dump as insert statements, and you (or whoever Googles this) are attempting to figure out the date and time, or translate it for use elsewhere (eg: to convert to MySQL inserts). This is actually easy in any programming language.
Let's work with this:
CAST(0x0000A61300B1F1EB AS DateTime)
This Hex representation is actually two separate data elements... Date and Time. The first four bytes are date, the second four bytes are time.
The date is 0x0000A613
The time is 0x00B1F1EB
Convert both of the segments to integers using the programming language of your choice (it's a direct hex to integer conversion, which is supported in every modern programming language, so, I will not waste space with code that may or may not be the programming language you're working in).
The date of 0x0000A613 becomes 42515
The time of 0x00B1F1EB becomes 11661803
Now, what to do with those integers:
Date
Date is since 01/01/1900, and is represented as days. So, add 42,515 days to 01/01/1900, and your result is 05/27/2016.
Time
Time is a little more complex. Take that INT and do the following to get your time in microseconds since midnight (pseudocode):
TimeINT=Hex2Int(HexTime)
MicrosecondsTime = TimeINT*10000/3
From there, use your language's favorite function calls to translate microseconds (38872676666.7 µs in the example above) into time.
The result would be 10:47:52.677
Some of them actually does covert to a date-time from SQL Server 2008 onwards.
Try the following SQL query and you will see for yourself:
SELECT CAST (0x00009CEF00A25634 AS datetime)
The above will result in 2009-12-30 09:51:03:000 but I have encountered ones that actually don't map to a date-time.
Not sure if I'm missing something here but can't you just convert the timestamp like this:
CONVERT(VARCHAR,CAST(ZEIT AS DATETIME), 110)

Resources