SSIS/SQL - Convert Date/Time to String Failing - sql

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

Related

Error converting data type varchar to date converting an int year into a string

I have SchoolYear variable as an INT, and I'm trying to set a variable to this:
SET #BeginDate = '07/01/' + CONVERT(VARCHAR(4), #SchoolYear - 1)
It gives me the 'Error converting data type varchar to date' error.
Example:
#SchoolYear INT = 2019,
#BeginDate Date - NULL
Desired result:
07/01/2018
What am I doing wrong please?
Check out DATEFROMPARTS (available from SQL 2012), and bypass strings altogether.
SET #BeginDate = DATEFROMPARTS(#SchoolYear - 1, 1, 7)
1st arg = year, 2nd = month, 3rd = day.
The format you are using is simply not understood by your sql server.
When dealing with dates in varchar columns/variables it is best to use a date format that is language neutral.
yyyyMMdd is such a format, it will always work no matter what regional settings are used on your server.
See also this http://karaszi.com/the-ultimate-guide-to-the-datetime-datatypes
in your case you should use
SET #BeginDate = CONVERT(VARCHAR(4), #SchoolYear - 1) + '0107'
that is assuming that 01 is the month, and 07 is the day so you end up with 20180107
Better would be off course to avoid varchar complete when converting, like this
set #BeginDate = DATEFROMPARTS(#SchoolYear - 1, 1, 7)
Maybe you should use the yyyy-mm-dd format and change the set as:
SET #BeginDate = CONVERT(VARCHAR(4), #SchoolYear - 1)+ '-01-07'
Declare #SchoolYear int
Set #SchoolYear = 2019
Declare #BeginDate varchar(50)
SET #BeginDate = '07/01/' + CONVERT(VARCHAR(4), #SchoolYear - 1)
--Once You have you varchar populated, you can use Convert Function to convert
--to datetime and select the format you want
Select CONVERT (Datetime,#BeginDate, 101)

Is it possible to set 2 variables with one set in T-SQL

Good morning Stack overflow.
At the top of one of my stored procedures I have 2 variables for my dates I use.
#StartDate is for the start of the current day and #EndDate is for the current time of the day.
DECLARE #StartDate DATE
DECLARE #EndDate DATETIME
SET #StartDate = GETDATE()
SET #EndDate = GETDATE()
PRINT #StartDate
PRINT #EndDate
I was wondering if it is possible to set both #StartDate and #EndDate to GETDATE() in the same line?
Thank you
I'll put you out of your misery: No, you can't. My guess here is that either you want both variables to be exactly the same (eliminating any possibility of two calls to GETDATE() returning different values, however small), or you just want fewer function calls. SQL Server doesn't support syntax like
SET #StartDate = #EndDate = GETDATE()
That's even leaving aside the implicit narrowing from DATETIME to DATE (althoguh that's not really an issue). Best you can do is
SET #StartDate = GETDATE()
SET #EndDate = #StartDate
Unless you just want to save a line in which case you can comma separate the #Variable = GETDATE() calls and only use one SELECT.
EDIT
Forgot to add this example - could reduce to one line thus:
SELECT #EndDate = GETDATE(), #StartDate = #EndDate
SET can only assign one variable at a time, but SELECT multiple.
so that's what you want:
SELECT #StartDate = GETDATE(), #EndDate = GETDATE()

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

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.

Transforming nvarchar day duration setting into datetime

I have a SQL Server function which converts a nvarchar day duration setting into a datetime value.
The day duration format is >days<.>hours<:>minutes<, for instance 1.2:00 for one day and two hours.
The format of the day duration setting can not be changed, and we can be sure that all data is correctly formatted and present.
Giving the function a start time and the day duration setting it should return the end time.
For instance: 2010-01-02 13:30 ==> 2010-01-03 2:00
I'm using a combination of charindex, substring and convert methods to calculate the value,
which is kind of slow and akward. Is there any other way to directly convert this day duration setting into a datetime value?
Not from what I can see. I would end up with a similar bit of SQL like you, using charindex etc. Unfortunately it's down to the format the day duration is stored in. I know you can't change it, but if it was in a different format then it would be a lot easier - the way I'd usually do this for example, is to rationalise the duration down to a base unit like minutes.
Instead of storing 1.2:00 for 1 day and 2 hours, it would be (1 * 24 * 60) + (2 * 60) = 1560. This could then be used in a straightforward DATEADD on the original date (date part only).
With the format you have, all approaches I can think of involve using CHARINDEX etc.
One alternative would be to build a string with the calculation. Then you can run the generated SQL with sp_executesql, specifying #enddate as an output parameter:
declare #startdate datetime
declare #duration varchar(10)
declare #enddate datetime
set #startdate = '2010-01-02 13:30'
set #duration = '0.12:30'
declare #sql nvarchar(max)
set #sql = 'set #enddate = dateadd(mi,24*60*' +
replace(replace(#duration,'.','+60*'),':','+') + ', #startdate)'
exec sp_executesql #sql,
N'#startdate datetime, #enddate datetime out',
#startdate, #enddate out
This creates a string containing set #enddate = dateadd(mi,24*60*0+60*12+30, #startdate) and then runs it.
I doubt this is faster than the regular charindex way:
declare #pos_dot int
declare #day int
declare #hour int
declare #minute int
select
#pos_dot = charindex('.',#duration),
#day = cast(left(#duration, #pos_dot-1) as int),
#hour = cast(left(right(#duration, 5), 2) as int),
#minute = cast(right(#duration, 2) as int),
#enddate = dateadd(mi, 24*60*#day + 60*#hour + #minute, #startdate)

Limiting a date range with exactness in MS SQL / SQL Server 2005

I want to limit a report to return records from Date A through Date B. This is what I have been doing:
declare #startDate varchar(20)
declare #endDate varchar(20)
set #startDate = '01/01/2008'
set #endDate = '04/01/2008'
-- test what are the start and end dates
select min(date),max(date) from view_Inspections
where date between #startDate and #endDate
... which I was told returned records from 12 am Jan 1st through 11:59 pm March 31st (that midnight is the default when no time is indicated). But I noticed a discrepancy, which is if a record has a time of 00:00:00 that it will be part of this set.
Is there a more exact way of doing this so it will return exactly the date range I want?*
I tried using time:
declare #startDate varchar(20)
declare #endDate varchar(20)
set #startDate = '01/01/2008 00:00:00'
set #endDate = '04/01/2008 11:59:59'
-- test what are the start and end dates
select min(date),max(date) from view_Inspections
where date between #startDate and #endDate
... but I noticed something wonky: SQL Server will ROUND the hundreth-second up by half. So I get that April 1st record (ha! April Fool's record! grr) if I use any time later than 11:59:29. Why is that?
(I feel sure there is. I'm new at this. Thanks for your help!)
There's always the easy option:
declare #startDate varchar(20)
declare #endDate varchar(20)
set #startDate = '01/01/2008'
set #endDate = '04/01/2008'
-- test what are the start and end dates
select min(date),max(date) from view_Inspections
where date >= #startDate
and date < #endDate
I suspect that the date column in view_Inspections is a SmallDateTime data type. This data type has 1 minute accuracy, which explains your unexpected results (rounding the seconds to the nearest minute).
The method Roland Shaw suggests is the best way to modify your query to accommodate your requirements.
The BETWEEN operator is inclusive, which is why you're seeing the results that you are in your first query. The rounding that you're seeing in your second query is going to be dependent on what exact datetime data type you are using in your table. (BTW, I think you're confusing seconds with hundredths of seconds). It looks like you're probably using a smalldatetime in your table, in which case the time is rounded to the nearest minute.
If your table is using datetime, try explicitly converting your #startDate and #endDate to DATETIME values (CAST(#endDate AS DATETIME)).
A quick note... even for DATETIME values, SQL Server is only accurate to the 3/100ths of a second, so 11:59:59.999 will get rounded up to 12:00:00.000.
You basically have three choices:
1) BETWEEN CAST('01/01/2008 00:00:00.000' AS DATETIME) AND CAST('03/31/2008 12:59:59.997' AS DATETIME)
2) YEAR(my_date) = 2008 AND MONTH(my_date) BETWEEN 1 AND 3
3) my_date >= CAST('01/01/2008 00:00:00.000' AS DATETIME) AND my_date < CAST('04/01/2008 00:00:00.000' AS DATETIME)
The first method isn't very intuitive and is error-prone in my opinion. The second method kills performance since indexes can't be used and it becomes much more complex if you can have searches that span years or begin/end in the middle of months. The third method, which Rowland suggested, is the best I think.
Simply try removing the time from the date field like so:
declare #startDate varchar(20)
declare #endDate varchar(20)
set #startDate = '01/01/2008'
set #endDate = '04/01/2008'
SELECT min(date),max(date) FROM view_Inspections
WHERE CAST(FLOOR(CAST(date AS FLOAT)) AS DATETIME) BETWEEN CAST(#startDate AS DATETIME) And CAST(#startDate AS DATETIME))
This will return everything from 01/01/2008 00:00:00 to 04/01/2008 11:59:59.999.
If you don't want 04/01 included, change your end date to 03/31/2008.
Your best solution is just create a BIGINT(10) field that called "julian", and store it in YYYYMMDD.
Then do the query
where julian >= '20120103' AND julian <= '20120203'