Converting date and/or time from character string is failing - sql

I have this query and I tried converting it to every format, I mean the date time etc but it doesn't work and throws error:
Conversion failed when converting date and/or time from character string.
SELECT W.Organization_ID,
W.NIT_No,
W.SchemeID,
OpeningDate,
OpeningTime,
GETDATE(),
WorkNo,
CONVERT(decimal(10, 2), W.Cost) AS Cost,
WorkName,
W.ExpiryDate as ExpiryDate,
CONVERT(VARCHAR,OpeningDate,106),
CASE WHEN
CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' '
+ CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
FROM Works W
the CASE part throws error.
OpeningDate is of type Varchar and OpeningTime is of type Time.
Why?

You are converting just a TIME datatype not a DATETIME so you don't need to specify the style:
DECLARE #T TIME = '08:05:06';
SELECT CONVERT(VARCHAR(8), #T) AS [Time];
SELECT CAST(#T AS VARCHAR(8)) AS [Time];
Or since you are using CONVERT()pick the right style for TIME datatype which is 108 or 114, instead of 106
SELECT CONVERT(VARCHAR(8), #T, 108) AS [Time];
Update:
According to the error Msg, your problem is in the CASE part.
That because you are trying to concat a DATETIME with a VARCHAR datatype, look at here to what you'r converting:
CASE WHEN
CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' '
+ CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
Also the column OpeningDate -according to the error Msg- is VARCHAR so your are convert a VARCHAR to VARCHAR then convert it again to DATETIME then you try to concatinate the DATETIME with the VARCHAR returned from converting OpeningTime column from TIMEto VARCHAR, then try to compare them with GETDATE() which is DATETIME datatype.
So you CASE should look like:
CASE WHEN
(
CAST(OpeningDate AS DATETIME) + -- VARCHAR to DATETIME
CAST(OpeningTime AS DATETIME) -- TIME to DATETIME
) < GETDATE()
THEN 1
ELSE 0 END AS OpeningVaild
A beside note, here in this line
CONVERT(VARCHAR,OpeningDate,106),
You are trying to convert a VARCHAR to VARCHAR and without specify the lenght too, so this line should be:
CONVERT(VARCHAR(10),CAST(OpeningDate AS DATE),106),
Finally, don't ever ever store DATE as VARCHAR, the DATE/ TIME/ DATEIME are there for a reason, so use them and all other datatypes wisely.
Here is a demo represent your issue, and how to fix it.

You can simplify this big time by changing the expression a little.
This way you don't have to convert and concatenate.
SELECT
CASE WHEN OpeningDate < GETDATE() - OpeningTime
THEN 1
ELSE 0 END AS OpeningVaild
Note I am assuming that Openingdate has the format dd-mon-yyyy. Otherwise you still need to convert it, but still shorter:
SELECT
CASE WHEN Convert(date, OpeningDate, 106) < GETDATE() - OpeningTime
THEN 1
ELSE 0 END AS OpeningVaild

So I understand the problem is with this part:
CASE WHEN CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' ' + CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE() THEN 1 ELSE 0 END AS OpeningVaild
Update
Since I've first posted my answer it turns out that you store the opening date as varchar instead of date.
First, you should stop doing that. Never store dates in anything other than a Date column (unless you need them with time as well, and then use DateTime2).
For more information, read Aaron Bertrand's Bad habits to kick : choosing the wrong data type.
Assuming the data type of the column can't change, you wrote in the comments to the question:
#ZoharPeled: this is the format of openingdate 2017-04-10
Illustrating one of the problems caused by storing dates as strings - How can I, or anyone else for that matter, know if that's the 10th of April or the 4th of October? The answer is we can't.
So, assuming it's the 10th of April, you can convert it to DateTime using convert with 126 as the style parameter:
CASE
WHEN CONVERT(DateTime, OpeningDate, 126) + CAST(OpeningTime As DateTime) < GETDATE() THEN
1
ELSE
0
END As OpeningVaild
First version:
Assuming that the data type of OpeningDate is Date and the data type of OpeningTime is Time, Seems like you are attempting to figure out if these columns combination into a DateTime is before the current DateTime.
Instead of converting them into strings and back to DateTime, you can cast both to DateTime and simply add them together:
CASE
WHEN CAST(OpeningDate As DateTime) + CAST(OpeningTime As DateTime) < GETDATE() THEN
1
ELSE
0
END As OpeningVaild
Another option would be to use GETDATE() twice. I don't think it should matter in the select clause, but in the where clause it's important to use this option since the first one will make these columns non-seargable, meaning the database engine will not be able to use any indexes that might help the execution plan of the statement:
CASE
WHEN OpeningDate < CAST(GETDATE() AS DATE)
OR
(
OpeningDate = CAST(GETDATE() AS DATE)
AND OpeningTime <= CAST(GETDATE() AS TIME)
) THEN
1
ELSE
0
END AS OpeningVaild
That being said, your query also have CONVERT(VARCHAR,OpeningDate,106) - The 106 style returns a string representation of the date as dd mon yyyy - meaning 11 chars - so change that to CONVERT(CHAR(11),OpeningDate,106) Note that using varchar without specifying the length defaults to 30, which is not a problem in this case since it's more than he 11 chars you need, but it's a bad habit to not specify length and you should kick it.

Related

Handling a value of zero in a date datatype

I'm reporting out of a database that is using decimal(17,6) as the datatype for a date field. For example, the current date/time in this field would be 20210820.171900. Unusual, but whatever. I need to convert the original date field from decimal(17,6) to datetime. This is what I have:
SELECT convert(datetime, convert(varchar,convert(int, lastmoddatetime)), 0)
from Table1
The above statement works correctly as long as none of the records have a value of zero in this column. Unfortunately, the column value defaults to zero (0.000000) if no date has been calculated for it. Whenever a column has a zero value, I get the following error:
Conversion failed when converting date from character string.
How can I overcome this issue? Ultimately, I'm needing to apply a dateadd function to the lastmoddatetime field.
Note: Before you suggest changing the column definition, this database originated in the 1990's and I'm not allowed to make any changes to the database structure.
You can use NULLIF to null out those values
convert(datetime, convert(varchar(15), convert(int, NULLIF(lastmoddatetime, 0.0))), 0)
Either use TRY_CONVERT or CASE - depending how you want to handle the zero case.
SELECT
-- If desiring null for 0 and SQL Server 2012+
TRY_CONVERT(date, CONVERT(varchar, CONVERT(int, lastmoddatetime)), 0)
, CASE WHEN lastmoddatetime <> 0
-- If desiring some other valid date or < SQL Server 2012
THEN CONVERT(date, CONVERT(varchar, CONVERT(int, lastmoddatetime)), 0)
ELSE NULL /* Whatever valid datetime value you want */ END
FROM (
VALUES (20210820.171900), (0.0)
) x (lastmoddatetime);
I note that this ignores the time component - so am converting to a date not datetime above. If you need to handle the time component you need to update your question.
Yet another option.
You can thin it out a bit by using left() and try_convert()
Example
Declare #YourTable table (lastmoddatetime numeric(17,6))
Insert into #YourTable values
(20210820.171900)
,(0.0)
Select AsDate = try_convert(date,left(lastmoddatetime,8))
,AsDateTime = try_convert(datetime,left(lastmoddatetime,8))
From #YourTable
Results
AsDate AsDateTime
2021-08-20 2021-08-20 00:00:00.000
NULL NULL
use
convert(datetime,convert(int,lastmoddatetime),0)

Difference Between two dates or N/A

I have a calculation between two dates.
What I'm trying to do is if the start date is before 03/07/20 then show N/A otherwise the difference between the two dates
case
when StartDate < cast('03-07-20' as date) then
'N/A'
else
DATEDIFF(day, cast(StartDate as date), cast(SwabDate as date) )
end
as Days_From_First
I get
Conversion failed when converting the varchar value 'N/A' to data type int.
Warning: Null value is eliminated by an aggregate or other SET operation.
Thank you for your help
All branches of a case expression must return the same datatype, so you can't have a branch return a string ('NA') and the other a integer (as returned by datediff()). What happens when you do that is that SQL Server prioritizes the numeric datatype, and hence attempts to coerce 'NA' to an integer - which fails.
You could cast the return value of datediff() to a string - but I would not recommend that. Probably, using null is the best way to go here: in SQL, that's usually how we represent the absence of data:
case when StartDate >= '20200703'
then datediff(day, cast(startdate as date), cast(swabdate as date))
end as Days_From_First
Note that I changed the date comparison to use literal '20200703', that SQL Server is able to unambiguously understand as a date in format YYYYMMDD.
You need to cast the results to be strings:
(case when StartDate < cast('2020-03-07' as date)
then 'N/A'
else convert(varchar(255), datediff(day, cast(StartDate as date), cast(SwabDate as date) ))
end) as Days_From_First
That said, I would really suggest that you forget about 'N/A' and just use NULL.
Also, I don't know if your date is 2020-03-07 or 2020-07-03. That is why you should use standard date formats.

Convert and Operation Not working as expected. Ignoring Year value

I have the following code:
CONVERT(VARCHAR(20), TimeCard_Date, 101) <
CONVERT(VARCHAR(20), dateadd(dd,-3,getdate()), 101)
The Original TimeCard_Date value = 2018-06-01
The GetDate() return = 11/14/2017
Can anyone assist as to why it thinks the Timecard_Date value set for June 2018 is less than the GetDate() minus 3 days value?
When you convert, it converts to a varchar datatype. 06/01/2018 is less than 11/14/2017 as a varchar since it is an alphabetical (or by number?) comparison. If you compare by date, the comparison is by the date datatype, which is as you expect.
You can change your code to:
TimeCard_Date < dateadd(dd,-3,getdate())
You don't need to convert DATETIME to VARCHAR in order to compare dates. Just use:
TimeCard_Date < DATEADD(dd,-3,GETDATE())
On the other hand, if you ever have to convert them to do it, you have to standarize the format (yyyyMMdd). You can check the FORMAT function https://learn.microsoft.com/en-us/sql/t-sql/functions/format-transact-sql

Convert date format doesn't take effect on self made date string in SQL Server

I have a rather strange issue here. I have a date string, which I've created partly by myself to incorporate a variable. The problem is, that I'm setting another language settings. In this case, I have to also convert the string to fit the language settings format. I'm using this code:
cast(convert(varchar, cast(cast(getdate() as date) as varchar) + ' ' + RIGHT('0' + CAST(#HR as varchar), 2) + ':00:00.000', 120) as datetime)
I get the error "The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.", which is normal if you assign wrong date format before casting.
The problem is, that when I try to convert the personally created date string, it doesn't change its format no matter what format code I set it in. That doesn't change even when I hardcode a number instead of my variable:
convert(varchar, cast(cast(getdate() as date) as varchar) + ' 0' + CAST(2 as varchar) + ':00:00.000', 101)
results in 2016-09-14 02:00:00.000
For example,
convert(varchar, dateadd(Hour, 2, getdate()), 101) as datetime
Results in 09/14/2016.
Even though I have a different language setting, isn't SQL server supposed to always recognize the date format in the standard format?
Please give me an advice so I can overcome this issue.
Thanks in advance!
PS: I managed to solve my issue by inserting converted datetime column in a variable before setting bulgarian language. I'm still very interested to know what causes the problem though.
Ok I may have a soution for the question: Why is the format differently handled in SQL-SERVER when converting.
CONVERT(data_type(length),expression,style)
The STYLEvalue only applies for date/time.
So it's because of the datatype that the output is different.
See following example:
SELECT convert(varchar, dateadd(Hour, 2, getdate()), 101) as datetime
You get the result:
09/14/2016
Here your are converting a datetime datatype into a varchar and the STYLE-value with 101 applies for CONVERT and the output is converted in that format.
Example 2 is the same but the inner most part is casted into a varchar before converting it:
SELECT convert(varchar, CAST(dateadd(Hour, 2, getdate()) AS varchar), 101) as datetime
The result you get is:
Sep 14 2016 4:09PM
So because we are trying to convert a varchar into a varchar the STYLE-value doesn't apply for the conversion.
That is also why the first query is handled diffrent then the other:
SELECT convert(varchar, cast(cast(getdate() as date) as varchar) + ' 0' + CAST(2 as varchar) + ':00:00.000', 101)
Here you cast into varchar cast(cast(getdate() as date) as varchar) before converting. So the STYLE-value is not applying because it's not from datatype date/time.
I hope it made it a bit clear. Let me know if this helped.
When you use convert to format the datetime, you can pass a style number to it.
Looky here or here for those numbers.
The query below converts custom created datetimes to the 126 (ISO8601) format.
declare #d int = 2;
SELECT
CONVERT(varchar,
CONVERT(datetime,
CONCAT(FORMAT(GETDATE(),'yyyy-MM-dd'),' ',#d,':0')
)
,126) AS MyDateStamp1,
CONVERT(varchar,
CONVERT(datetime,
CONVERT(varchar,GETDATE(),102)+' '+convert(varchar,#d)+':0'
)
,126) AS MyDateStamp2;
The FORMAT & CONCAT functions can be used in SQL Server 2012 and beyond.
But if you have an earlier version then CONVERT should work instead.
Additional tip:
If you're using the CONVERT solution above, note that
"convert(varchar, CAST(dateadd(Hour, 2, getdate()) AS varchar), 101)" calls for you to set datatype to varchar.
I just came across code
"Convert(date,ML.StartDate,101)"
and since style 101 is mm/dd/yyyy, and the output was yyyy-mm-dd, I knew something was wrong. By changing the code to
"Convert(varchar,ML.StartDate,101)"
the proper date style was displayed in the result set.

Combining (concatenating) date and time into a datetime

Using SQL Server 2008, this query works great:
select CAST(CollectionDate as DATE), CAST(CollectionTime as TIME)
from field
Gives me two columns like this:
2013-01-25 18:53:00.0000000
2013-01-25 18:53:00.0000000
2013-01-25 18:53:00.0000000
2013-01-25 18:53:00.0000000
.
.
.
I'm trying to combine them into a single datetime using the plus sign, like this:
select CAST(CollectionDate as DATE) + CAST(CollectionTime as TIME)
from field
I've looked on about ten web sites, including answers on this site (like this one), and they all seem to agree that the plus sign should work but I get the error:
Msg 8117, Level 16, State 1, Line 1
Operand data type date is invalid for add operator.
All fields are non-zero and non-null. I've also tried the CONVERT function and tried to cast these results as varchars, same problem. This can't be as hard as I'm making it.
Can somebody tell me why this doesn't work? Thanks for any help.
Assuming the underlying data types are date/time/datetime types:
SELECT CONVERT(DATETIME, CONVERT(CHAR(8), CollectionDate, 112)
+ ' ' + CONVERT(CHAR(8), CollectionTime, 108))
FROM dbo.whatever;
This will convert CollectionDate and CollectionTime to char sequences, combine them, and then convert them to a datetime.
The parameters to CONVERT are data_type, expression and the optional style (see syntax documentation).
The date and time style value 112 converts to an ISO yyyymmdd format. The style value 108 converts to hh:mi:ss format. Evidently both are 8 characters long which is why the data_type is CHAR(8) for both.
The resulting combined char sequence is in format yyyymmdd hh:mi:ss and then converted to a datetime.
The simple solution
SELECT CAST(CollectionDate as DATETIME) + CAST(CollectionTime as DATETIME)
FROM field
An easier solution (tested on SQL Server 2014 SP1 CU6)
Code:
DECLARE #Date date = SYSDATETIME();
DECLARE #Time time(0) = SYSDATETIME();
SELECT CAST(CONCAT(#Date, ' ', #Time) AS datetime2(0));
This would also work given a table with a specific date and a specific time field. I use this method frequently given that we have vendor data that uses date and time in two separate fields.
Cast it to datetime instead:
select CAST(CollectionDate as DATETIME) + CAST(CollectionTime as TIME)
from field
This works on SQL Server 2008 R2.
If for some reason you wanted to make sure the first part doesn't have a time component, first cast the field to date, then back to datetime.
DECLARE #ADate Date, #ATime Time, #ADateTime Datetime
SELECT #ADate = '2010-02-20', #ATime = '18:53:00.0000000'
SET #ADateTime = CAST (
CONVERT(Varchar(10), #ADate, 112) + ' ' +
CONVERT(Varchar(8), #ATime) AS DateTime)
SELECT #ADateTime [A nice datetime :)]
This will render you a valid result.
Solution (1): datetime arithmetic
Given #myDate, which can be anything that can be cast as a DATE, and #myTime, which can be anything that can be cast as a TIME, starting SQL Server 2014+ this works fine and does not involve string manipulation:
CAST(CAST(#myDate as DATE) AS DATETIME) + CAST(CAST(#myTime as TIME) as DATETIME)
You can verify with:
SELECT GETDATE(),
CAST(CAST(GETDATE() as DATE) AS DATETIME) + CAST(CAST(GETDATE() as TIME) as DATETIME)
Solution (2): string manipulation
SELECT GETDATE(),
CONVERT(DATETIME, CONVERT(CHAR(8), GETDATE(), 112) + ' ' + CONVERT(CHAR(8), GETDATE(), 108))
However, solution (1) is not only 2-3x faster than solution (2), it also preserves the microsecond part.
See SQL Fiddle for the solution (1) using date arithmetic vs solution (2) involving string manipulation
Concat date of one column with a time of another column in MySQL.
SELECT CONVERT(concat(CONVERT('dateColumn',DATE),' ',CONVERT('timeColumn', TIME)), DATETIME) AS 'formattedDate' FROM dbs.tableName;
drop table test
create table test(
CollectionDate date NULL,
CollectionTime [time](0) NULL,
CollectionDateTime as (isnull(convert(datetime,CollectionDate)+convert(datetime,CollectionTime),CollectionDate))
-- if CollectionDate is datetime no need to convert it above
)
insert test (CollectionDate, CollectionTime)
values ('2013-12-10', '22:51:19.227'),
('2013-12-10', null),
(null, '22:51:19.227')
select * from test
CollectionDate CollectionTime CollectionDateTime
2013-12-10 22:51:19 2013-12-10 22:51:19.000
2013-12-10 NULL 2013-12-10 00:00:00.000
NULL 22:51:19 NULL
This works in SQL 2008 and 2012 to produce datetime2:
declare #date date = current_timestamp;
declare #time time = current_timestamp;
select
#date as date
,#time as time
,cast(#date as datetime) + cast(#time as datetime) as datetime
,cast(#time as datetime2) as timeAsDateTime2
,dateadd(dayofyear,datepart(dayofyear,#date) - 1,dateadd(year,datepart(year,#date) - 1900,cast(#time as datetime2))) as datetime2;
dealing with dates, dateadd must be used for precision
declare #a DATE = getdate()
declare #b time(7) = getdate()
select #b, #A, GETDATE(), DATEADD(day, DATEDIFF(day, 0, #a), cast(#b as datetime2(0)))
I am using SQL Server 2016 and both myDate and myTime fields are strings. The below tsql statement worked in concatenating them into datetime
select cast((myDate + ' ' + myTime) as datetime) from myTable
SELECT CONVERT(DATETIME, CONVERT(CHAR(8), date, 112) + ' ' + CONVERT(CHAR(8), time, 108))
FROM tablename