SQL - Date Query Issue - varchar to datetime conversion resulted in out-of-range value - sql

I have a SQL query written by a colleague who is no longer here. The query runs as part of an SSIS job, and as of this month has started failing with the following error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
The query itself is just a basic select with a where clause that looks for values in a certain time range (the date range between #startdate and #enddate)
The code to determine the time range is below:
DECLARE #RunDateTime datetime
DECLARE #RunDate datetime
DECLARE #StartDate datetime
DECLARE #EndDate datetime
DECLARE #Month int
DECLARE #Year int
DECLARE #strStartDate varchar(10)
SET #RunDateTime = GetDate()
SET #RunDate = cast(round(convert(real, #RunDateTime),0,1) as datetime)
IF DATEPART(d, #RunDate) = 16
BEGIN
SET #StartDate = DATEADD(d, -15, #RunDate)
SET #EndDate = #RunDate
END
ELSE
BEGIN
IF Month(#RunDate) = 1
SET #Month = 12
ELSE
SET #Month = Month(#RunDate) - 1
IF Month(#RunDate) = 1
SET #Year = Year(#RunDate) - 1
ELSE
SET #Year = Year(#RunDate)
SET #strStartDate = CONVERT(varchar(2), #Month)+ '/16/' + CONVERT(varchar(4), #Year)
SET #StartDate = CONVERT(datetime, #strStartDate, 101)
SET #EndDate = #RunDate
END
This job runs twice a month. Once on the 16th for data from the 1st to the 15th of the month, and once on the 1st of the next month for data from the 16th to the end of the previous month.
From what I can find online, the use of the varchar for strStartDate is the likely culprit? I'm not familiar enough with SQL to know how to replace all that convert stuff that's going on there? Also, is there a better way to determine the end of the month date than just getting the run time? Any help at all would be greatly appreciated.
(PS, we run this job on SQL Server 2008 R2) And I checked with the DBA and he said nothing about localization or regional settings has changed on the SQL server.

There are many formats supported by SQL Server - see the MSDN Books Online on CAST and CONVERT. Most of those formats are dependant on what settings you have - therefore, these settings might work some times - and sometimes not.
The way to solve this is to use the (slightly adapted) ISO-8601 date format that is supported by SQL Server - this format works always - regardless of your SQL Server language and dateformat settings.
The ISO-8601 format is supported by SQL Server comes in two flavors:
YYYYMMDD for just dates (no time portion); note here: no dashes!, that's very important! YYYY-MM-DD is NOT independent of the dateformat settings in your SQL Server and will NOT work in all situations!
or:
YYYY-MM-DDTHH:MM:SS for dates and times - note here: this format has dashes (but they can be omitted), and a fixed T as delimiter between the date and time portion of your DATETIME.
This is valid for SQL Server 2000 and newer.
If you use SQL Server 2008 or newer and the DATE datatype (only DATE - not DATETIME!), then you can indeed also use the YYYY-MM-DD format and that will work, too, with any settings in your SQL Server.
Also: with SQL Server 2008, it is recommended to use DATETIME2 (instead of DATETIME) if at all ever possible. DATETIME2 parsing of strings is a lot more forgiving for error and/or different formats (like US AM/PM formatting etc.) .
Don't ask me why this whole topic is so tricky and somewhat confusing - that's just the way it is. But with the YYYYMMDD format, you should be fine for any version of SQL Server and for any language and dateformat setting in your SQL Server.

Related

Converting Date/Time from String Error When Using Cast

I am getting a "Unable to Convert Date/Time from Character String" error message when I run my Access query, but I am not sure why. I have used this method before and have not had any trouble.
SET NOCOUNT ON
DECLARE #StartDate date = '[Start date]', #EndDate date = '[End date]'
SELECT
CAST (Date as Date) as LocalDay
,SalesID
,Status
,Wait
,PO_Number
FROM
cpo_test.dbo.table_agent_detail_view
WHERE Date BETWEEN #StartDate And #EndDate
AND SOURCEID=1
Why is Cast not working in this instance?
The problem is not with the CAST, it's with
DECLARE #StartDate date = '[Start date]', #EndDate date = '[End date]'
I get the same error in SSMS if I try to execute that statement alone. The problem goes away if I change it to
DECLARE #StartDate date = '2018-01-01', #EndDate date = '2018-12-31'
It's not clear what you are trying to accomplish by assigning the string value '[Start date]' to a date variable, but you're going to have to figure out another way to do it.
Note: You call it your "Access query" but it is not Access SQL, it's T-SQL (SQL Server) so I assume that you're running it as a pass-through query in Access. If so, then be aware that pass-through queries behave quite differently from regular Access queries because they are not processed by the Access expression evaluator or the Access Database Engine, they are passed directly to the ODBC data source (hence the name "pass-through query").
I think you are missing a cast in Date near where condition on your query.
Try
SELECT
CAST (Date as Date) as LocalDay
,SalesID
,Status
,Wait
,PO_Number
FROM
cpo_test.dbo.table_agent_detail_view
WHERE CAST (Date as Date) BETWEEN #StartDate And #EndDate
AND SOURCEID=1

dateadd - finding a date in the past

I have an odd problem with subtracting 18 months from a date.
Consider the following snippet:
Convert(dateTime,CREATION_DATE,103) >
DateAdd (MONTH, -18, Convert (DateTime, #IN_ACTIVITY_DATE, 103))
This forms part of a where clause in a stored procedure. When I run:
exec theSP #IN_ACTIVITY_DATE = '21/02/2013'
it runs fine, but when I change it to this:
exec theSP #IN_ACTIVITY_DATE = '21/01/2013'
it breaks with the error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Does anyone have any thoughts on why it might be January (all other months are ok) that is breaking my code?
Thanks.
DS
To pass string as datetime use ISO format as yyyymmdd which guaranteed to work in any server with any culture info.;
exec theSP #IN_ACTIVITY_DATE = '20130121'
Also as a side note, if CREATION_DATE is already datetime then you don't need to CONVERT CREATION_DATE for comparison.
--if CREATION_DATE is DATETIME/DATE column
CREATION_DATE >
DateAdd (MONTH, -18, Convert (DateTime, #IN_ACTIVITY_DATE))
Specify the date in YYYY-MM-DD format.
Eg.:
exec theSP #IN_ACTIVITY_DATE = '2013-01-21'
Also, you can use SET DATEFORMAT to specify the date format:
Eg.:
SET DATEFORMAT dmy;
exec theSP #IN_ACTIVITY_DATE = '21/02/2013'
exec theSP #IN_ACTIVITY_DATE = '21/01/2013'
SET DATEFORMAT: Sets the order of the month, day, and year date parts for interpreting date, smalldatetime, datetime, datetime2 and datetimeoffset character strings.
SET DATEFORMAT { format | #format_var }
format | #format_var: Is the order of the date parts. Valid parameters are mdy, dmy, ymd, ydm, myd, and dym.
There might be something in your database, perhaps a check constraint, that doesn't like the January date. The conversion to seems to work on SQL Server 2008 (Fiddle). Something else might be causing the problem.
Also, the date formatting suggestions provided by the others are good advice.

SSIS/SQL - Convert Date/Time to String Failing

I have a basic SSIS job that runs a SQL query and exports the results of the query to an Excel file. The SSIS job has run without issue for over a year, but has started to fail (at validation phase, before execution) this month with the following error:
"Conversion Failed when converting date and/or time from character
string."
The SQL query itself is a simple SELECT with a WHERE clause to specify a start and end date range to gather records from. The part of the query that determines the date range is below:
DECLARE #RunDateTime datetime
DECLARE #RunDate datetime
DECLARE #StartDate datetime
DECLARE #EndDate datetime
DECLARE #Month int
DECLARE #Year int
DECLARE #strStartDate varchar(10)
SET #RunDateTime = GetDate()
SET #RunDate = CAST(#RunDateTime AS DATE)
IF DATEPART(d, #RunDate) = 16
BEGIN
SET #StartDate = DATEADD(d, -15, #RunDate)
SET #EndDate = #RunDate
END
ELSE
BEGIN
IF Month(#RunDate) = 1
SET #Month = 12
ELSE
SET #Month = Month(#RunDate) - 1
IF Month(#RunDate) = 1
SET #Year = Year(#RunDate) - 1
ELSE
SET #Year = Year(#RunDate)
SET #strStartDate = CAST(#Year AS VARCHAR) + CAST(#Month AS VARCHAR) + '16'
SET #StartDate = CONVERT(datetime, #strStartDate)
SET #EndDate = #RunDate
END
I can take this code out of SSIS and execute it directly on the server via SSMS, and it works correctly with no errors. However, the SSIS job cannot run it. In fact, I can't even go into the OLE DB Source object for the SQL command and view the column metadata anymore, I get the error mentioned above every time.
I have found that by commenting out the SET #StartDate = CONVERT(datetime, #strStartDate) line the query works again in SSIS, but there's no reason this line should cause problems. As I said, it has worked for over a year with no issues. I'm literally at my wits' end with this, and cannot understand why the job has suddenly stopped working.
I did check with my DBA, and he assures me nothing has been changed on the SQL server side, but then again I wouldn't even know where to ask him to look.
Other info:
The SSIS job is using the SQLNCLI10.1 connection provider. (2008 R2 SQL Server)
I've tried using other connection providers with no luck.
The date column involved in the query is of type datetime
There have been no regional/localization changes that would affect the dates. (And all the dates are ISO formatted).
This fails because the #strStartDate is not a valid date or is not in a recognized format. It shouldn't fail in November, December or January but it will definitely fail for February through October (Month(#RunDate) - 1) because the CAST(#Month AS VARCHAR) returns only a single digit.
To be honest this is is a rather long way of calculating the 16th of the previous month, can you change it to...
DECLARE #StartDate DateTime
IF DATEPART(d, GetDate()) = 16
SET #StartDate = DateAdd(mm, DateDiff(mm,0,GetDate()),0)
ELSE
SET #StartDate = DateAdd(mm, DateDiff(mm,0,GetDate())-1,0)+15;
So much easier

How to set part of a datetime

I need to set just the hour of a datetime to a certain value. I know I can add it the difference to the desired value, or create a new datetime with parts from the original one, but isn't there a cleaner way? Something like:
declare #d datetime = '09/08/2012 09:14:55'
set #d = SETDATEPARTORSOMETHINGLIKETHAT (hour, #d, 23)
Thanks a lot.
DECLARE #TargetHour TINYINT = 23;
DECLARE #d datetime = '09/08/2012 09:14:55';
SET #d = DATEADD(HOUR, #TargetHour - DATEPART(HOUR, #d), #d);
SELECT #d;
Result:
2012-09-08 23:14:55.000
(And yes, this will work if the #TargetHour is less than the current hour, e.g. 5 or 7.)
I'd be very careful though about using ambiguous and error-prone formats like m/d/y. Case in point: I don't even know if you meant September 8th or August 9th, and SQL Server isn't going to know either - it needs to use regional settings etc. to figure it out, and if you give your code to someone with different settings, it will generate an error or, even worse, accept the wrong date silently. You should use:
DECLARE #d datetime = '2012-09-08T09:14:55';
Bad habits to kick : mis-handling date / range queries

Convert varchar into datetime in SQL Server

How do I convert a string of format mmddyyyy into datetime in SQL Server 2008?
My target column is in DateTime
I have tried with Convert and most of the Date style values however I get an error message:
'The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.'
OP wants mmddyy and a plain convert will not work for that:
select convert(datetime,'12312009')
Msg 242, Level 16, State 3, Line 1
The conversion of a char data type to a datetime data type resulted in
an out-of-range datetime value
so try this:
DECLARE #Date char(8)
set #Date='12312009'
SELECT CONVERT(datetime,RIGHT(#Date,4)+LEFT(#Date,2)+SUBSTRING(#Date,3,2))
OUTPUT:
-----------------------
2009-12-31 00:00:00.000
(1 row(s) affected)
SQL Server can implicitly cast strings in the form of 'YYYYMMDD' to a datetime - all other strings must be explicitly cast. here are two quick code blocks which will do the conversion from the form you are talking about:
version 1 uses unit variables:
BEGIN
DECLARE #input VARCHAR(8), #mon CHAR(2),
#day char(2), #year char(4), #output DATETIME
SET #input = '10022009' --today's date
SELECT #mon = LEFT(#input, 2), #day = SUBSTRING(#input, 3,2), #year = RIGHT(#input,4)
SELECT #output = #year+#mon+#day
SELECT #output
END
version 2 does not use unit variables:
BEGIN
DECLARE #input CHAR(8), #output DATETIME
SET #input = '10022009' --today's date
SELECT #output = RIGHT(#input,4) + SUBSTRING(#input, 3,2) + LEFT(#input, 2)
SELECT #output
END
Both cases rely on sql server's ability to do that implicit conversion.
Likely you have bad data that cannot convert. Dates should never be stored in varchar becasue it will allow dates such as ASAP or 02/30/2009. Use the isdate() function on your data to find the records which can't convert.
OK I tested with known good data and still got the message. You need to convert to a different format becasue it does not know if 12302009 is mmddyyyy or ddmmyyyy. The format of yyyymmdd is not ambiguous and SQL Server will convert it correctly
I got this to work:
cast( right(#date,4) + left(#date,4) as datetime)
You will still get an error message though if you have any that are in a non-standard format like '112009' or some text value or a true out of range date.
I found this helpful for my conversion, without string manipulation. https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
CONVERT(VARCHAR(23), #lastUploadEndDate, 121)
yyyy-mm-dd hh:mi:ss.mmm(24h) was the format I needed.
Convert would be the normal answer, but the format is not a recognised format for the converter, mm/dd/yyyy could be converted using convert(datetime,yourdatestring,101) but you do not have that format so it fails.
The problem is the format being non-standard, you will have to manipulate it to a standard the convert can understand from those available.
Hacked together, if you can guarentee the format
declare #date char(8)
set #date = '12312009'
select convert(datetime, substring(#date,5,4) + substring(#date,1,2) + substring(#date,3,2),112)
Look at CAST / CONVERT in BOL that should be a start.
If your target column is datetime you don't need to convert it, SQL will do it for you.
Otherwise
CONVERT(datetime, '20090101')
Should do it.
This is a link that should help as well:
I'd use STUFF to insert dividing chars and then use CONVERT with the appropriate style. Something like this:
DECLARE #dt VARCHAR(100)='111290';
SELECT CONVERT(DATETIME,STUFF(STUFF(#dt,3,0,'/'),6,0,'/'),3)
First you use two times STUFF to get 11/12/90 instead of 111290, than you use the 3 to convert this to datetime (or any other fitting format: use . for german, - for british...) More details on CAST and CONVERT
Best was, to store date and time values properly.
This should be either "universal unseparated format" yyyyMMdd
or (especially within XML) it should be ISO8601: yyyy-MM-dd or yyyy-MM-ddThh:mm:ss More details on ISO8601
Any culture specific format will lead into troubles sooner or later...
use Try_Convert:Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
DECLARE #DateString VARCHAR(10) ='20160805'
SELECT TRY_CONVERT(DATETIME,#DateString)
SET #DateString ='Invalid Date'
SELECT TRY_CONVERT(DATETIME,#DateString)
Link:MSDN TRY_CONVERT (Transact-SQL)
I had luck with something similar:
Convert(DATETIME, CONVERT(VARCHAR(2), #Month) + '/' + CONVERT(VARCHAR(2), #Day)
+ '/' + CONVERT(VARCHAR(4), #Year))
The root cause of this issue can be in the regional settings - DB waiting for YYYY-MM-DD while an app sents, for example, DD-MM-YYYY (Russian locale format) as it was in my case. All I did - change locale format from Russian to English (United States) and voilĂ .
This seems the easiest way..
SELECT REPLACE(CONVERT(CHAR(10), GETDATE(), 110),'-','')
SQL standard dates while inserting or updating Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
So if you are inserting/Updating below 1/1/1753 you will get this error.
DECLARE #d char(8)
SET #d = '06082020' /* MMDDYYYY means June 8. 2020 */
SELECT CAST(FORMAT (CAST (#d AS INT), '##/##/####') as DATETIME)
Result returned is the original date string in #d as a DateTime.