The datediff function resulted in an overflow message - sql-server

doing something like this:
select *
from INVOICE_HEADING
where INVOICE_DATE >= '06 Dec 2018 00:00:00'
INVOICE_DATE <= '16 Dec 2018 00:00:00'
and I get this message:
"The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart."
How can I write it in a different way (or how can I use datediff here?) to get the result?

The function DATEDIFF returns a signed integer, which can hold values from -2.147.483.648 to 2.147.483.647. If the dates you are applying the function to and the unit you are using (month, day, second, etc.) generate a difference outside these bounds then an error is thrown.
There are a few workarounds:
Use DATEDIFF_BIG if you are using SQL Server 2016+.
Move to a "higher" unit (milliseconds -> seconds -> minutes -> hours and so on) until the value you get can be cast into a integer and make sure that all the values you might apply the function to in the future will still be inside the bounds of an integer. You can then drill down the unit to the one you need by multiplying and handling the value as BIGINT (for example).
It's common for this error to pop up when comparing dates that are not valid to the business or generated by default as 1900-01-01. You can filter these with a WHERE clause, supply a decent default value or convert to NULL. Can also avoid applying the DATEDIFF function with a CASE before it when dates aren't reasonable.
Examples:
DECLARE #OldDate DATE = '1900-01-01'
DECLARE #Now DATE = GETDATE()
SELECT DATEDIFF(SECOND, #OldDate, #Now) AS DateDiffResult
--Msg 535, Level 16, State 0, Line 5
--The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
Change the unit from second to minute:
DECLARE #OldDate DATE = '1900-01-01'
DECLARE #Now DATE = GETDATE()
SELECT DATEDIFF(MINUTE, #OldDate, #Now) AS DateDiffResult
-- DateDiffResult: 62599680
Revert the minute to second with a "bigger" data type:
DECLARE #OldDate DATE = '1900-01-01'
DECLARE #Now DATE = GETDATE()
SELECT
CONVERT(BIGINT, DATEDIFF(MINUTE, #OldDate, #Now)) * 60 AS DateDiffResult
-- DateDiffResult: 3755980800

Related

SQL query not returning all rows

I have a table in SQL Server which has many rows, with a created_date column. This column has rows starting from the year 2006.
I want to get all the rows which were created in and before February, 2015. This stored procedure has a parameter #month. It should select all the rows based on the #month value entered.
Here is my query:
select *
from products
where 1=1
and year(created_date) <= 2015
and month(created_date) <= #month
But this query returns only the records which were created in and before February month of previous years excluding records which were created in other months of 2014 (e.g., 2014-03-17, 2014-05-05 are excluded).
I have to get a new date based on the #month entered. Suppose I entered month July, I want to have condition "where created_date < 2015-07-31". So I can do something like this,
So I have changed my query,
declare #date datetime
set #date = CAST((2015 + '-' + #month + '-' + 28) as datetime)
select *
from products
where 1=1
and year(created_date) <= 2015
But this query returns 1905-08-08 00:00:00.000 and I want to get 2015-02-28 00:00:00.000 and also I have to find total number of days based on the #month entered so that I can pass that number to CAST((2015 + '-' + #month + '-' + 28) as datetime) instead of 28.
Just use a single date and specify that the created_date column must be less than that date:
declare #newestDate datetime = '2015-03-01'
select *
from products
where created_date < #newestDate
Note that I set the date to be the 1st March but in the query I use < rather than <=. This will cope with a created_date value including a time component, e.g. 2015-02-28 23:59:59
To generate the value of "February of previous year", you may actually be wanting to use the current month of last year, if so, your date would be:
declare #newestDate datetime =
DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE())+1, 0))
This would then work next month (i.e. March) and would give your query a rolling month.
Always compare date/time fields to a single value where possible -- this is best for performance as well. You can "round" dates with DATEADD and DATEDIFF.
DECLARE #startOfNextMonth DATETIME;
SELECT #startOfNextMonth = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0);
select * from products where 1=1 and created_date < #startOfNextMonth;
String manipulation to convert dates is also possible, but tends to perform worse and is tricky to get right. This technique applies in general if you want to "round" to years, minutes, 15-second periods, etcetera, which is much harder with strings.
If you can, rewrite your stored procedure to not take a #month parameter but an absolute value that your clients calculate -- it's more general and tends to be easier to work with. Your query then simply reduces to
select * from products where 1=1 and created_date < #limit;
Of course, if you must use a #month, you can construct this offset in the stored procedure itself:
DECLARE #limit DATETIME =
DATEFROMPARTS(DATEPART(YEAR, GETDATE()), #month, 1)
;
This takes advantage of DATEFROMPARTS, which was introduced with SQL Server 2012. For previous versions, reliably constructing a date is considerably messier. There are many wrong ways to do it that will break if the regional settings are set to something unexpected. DATEADD is again of assistance:
DECLARE #limit DATETIME =
DATEADD(MONTH, (DATEPART(YEAR, GETDATE()) - 1900) * 12 + #month - 1, 0)
;
These are not the only methods to construct datetime values, but string manipulation is in any case tricky (because the only reliable format that will not break under regional settings is YYYYMMDD, no dashes).
In this question: Create a date with T-SQL you will see how to construct an sql-server date data type given a certain year and month. Suppose you call that 'my_date'.
You will then be able to do the following:
SELECT * FROM products WHERE created_date < my_date

Why DATEDIFF in SQL Server returning error while interval is set to second

While running following query
select DATEDIFF(SECOND, 0, DATEADD(SECOND, -1, '2014-04-11 23:52'))
I am getting following error message, no matter whatever the date I provide to it.
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
Datediff takes these parameters: interval, starting_date, ending_date, so your SELECT is trying to find difference in seconds between server default for starting_date and your date.
When you specified 0 as starting_date, MS SQL replaced it with '1900-01-01 00:00'. The returned seconds where ~3606249060, but the DATEDIFF returns int, and the seconds returned where larger than datatype int could handle.
It works fine if you specify minute instead of second, because it'll return 60104151, which is int
You could use similar select to find difference in seconds between now and your defined date:
select DATEDIFF(SECOND, GETDATE(), DATEADD(SECOND,-1,'2014-04-11 23:52'))
If you put your hard-coded date as starting_date parameter, then you'll get -1 second difference (due to DATEADD you've used):
select DATEDIFF(SECOND, '2014-04-11 23:52', DATEADD(SECOND,-1,'2014-04-11 23:52'))
According to this DateDiff is not working because difference between 2 dates in Interval of Seconds is more than 68 years.
First argument in DateDiff as 0 means it is '1900-01-01 00:00:00.000', and difference with '2014-04-11 23:52' is more than 68 years.
Your current query results in a bigint, DATEDIFF can only return an integer. That is why you get overflow
3597523200 is the seconds between year 1900 and 2014
Try this instead:
SELECT 3597523200
+ DATEDIFF(S, '2014', DATEADD(SECOND,-1,'2014-04-11 23:52'))
This would be the same as:
SELECT CAST(DATEDIFF(S, '1900', '1950') AS BIGINT)
+ DATEDIFF(S, '1950', '2014')
+ DATEDIFF(S, '2014', DATEADD(SECOND,-1,'2014-04-11 23:52'))

SQL Date range using dynamic params

I have custom table, which stipulates in days how long to retain records for. I need to know how I can pass in a variable into a BETWEEN statement to returns only the records from today inclusive, -variable.
WHERE (MessageDate BETWEEN GETDATE() AND DATEADD(day, DATEDIFF(day, 0, GETDATE()), - 7))
-7 in this instance would be 7 days from today, and is meant to be a parameter which I pass in.
You don't need the DATEDIFF function, you can use just DATEADD, for which the correct parameter order you can find here.
So, your WHERE clause can look like this:
DECLARE #parameter INTEGER
SET #paramenter = -7
...
WHERE
(MessageDate BETWEEN GETDATE ()
AND DATEADD(day, #parameter, GETDATE())
)
Here is a SQLFiddle. (updated fiddle)
Edit:
Also, the BETWEEN part of the query should have an older date first ( DATEADD(day, -7, GETDATE() ) and then a more recent date ( GETDATE() ).
This is why, if you will always have a negative parameter to pass on, you will have to switch the order of the dates in the WHERE clause and use them like this:
WHERE
(MessageDate BETWEEN DATEADD(day, #parameter, GETDATE()
AND GETDATE())
)
But, if you might have to pass both positive and negative parameters, then use this:
WHERE
(MessageDate BETWEEN DATEADD(day, #parameter, GETDATE()
AND GETDATE())
OR
MessageDate BETWEEN GETDATE ()
AND DATEADD(day, #parameter, GETDATE())
)
Using OR you will have both cases covered, the one in which you send a positive parameter and the one where you send a negative parameter.
These cases are mutually exclusive, so only one condition will ever return results.
Substraction from GETDATE() results in DAYS, therefore you can use the following:
DECLARE #param INTEGER
SET #param = 7
--
WHERE cast(MessageDate as datetime) >= GETDATE()- #param
AND cast(MessageDate as datetime)<= GETDATE()

TSQL query for Week Comparison

I have a table that has a TASK_START_DATE and TASK_FINISH_DATE Columns of type datetime
I need help with a query that returns all Tasks when the Task: (date = just the date - I think I can do a conversion to the date from datetime on SQL 2008R2, it works fine)
- is within 2 weeks previous of the current date or two weeks after the current date.
Similarly I also need the records whose TaskEnd values are within 2 weeks previous or two weeks before
I've been trying things like which would get tasks where the start date is within the two previous weeks, but I have to do the same for TASK_FINISH_DATE and I think my and's and or's are all jumbled up, any help is appreciated.
Convert(Date, TASK_START_DATE) <= Convert(Date, DateAdd(ww, -2, GetDate()))
Short version:
How do I correctly write a query that combines all records with the TASK_START_DATE OR TASK_END_DATE within two weeks in the future or past, i.e.
Select Task_ID, TASK_NAME, TASK_START_DATE, TASK_END_DATE
where
???
You can add days to your date for comparision:
Select * from Table
Where column between getdate()-14 and getdate()+14
You don't need to use "Convert" function. "GetDate" function returns datetime value and your columns' types are datetime. You can add day number directly like this:
Select * from Table
Where (TASK_START_DATE between getdate() - 14 and getdate() + 14)
or (TASK_FINISH_DATE between getdate() - 14 and getdate() + 14)
You can declare variables or have the comparison dates right in the where clause. I use GETDATE() to get the date/time for right now as it returns a DATETIME object. Then I use DATEADD to adjust it for days, months, years, etc, and then you have to convert it to a DATE before sticking it in a variable of type DATE. Note in the DATEADD method I pass in the adjustment type (D = days), then adjust it + or - 14 days.
Alternatively you could just use 14 days ago to the minute if you don't do the DATE conversions...you'd have to remove the converts from the variable declarations as well as the where clause. Depends on the results you want though.
DECLARE #twoWeeksAgo DATE = CONVERT(DATE, DATEADD(D, -14, GETDATE()));
DECLARE #twoWeeksAhead DATE = CONVERT(DATE, DATEADD(D, 14, GETDATE()));
SELECT
Task_ID,
TASK_NAME,
TASK_START_DATE,
TASK_END_DATE
FROM
TABLE
WHERE
CONVERT(DATE, TASK_START_DATE) BETWEEN #twoWeeksAgo AND #twoWeeksAhead
OR CONVERT(DATE, TASK_END_DATE) BETWEEN #twoWeeksAgo AND #twoWeeksAhead
Also note that the BETWEEN operator in the WHERE clause is inclusive, meaning it will include records where the TASK_START_DATE is equal to the dates held by the variables. If you wanted to exclude records with the same value as #twoWeeksAhead, for example, you would have to use something like
WHERE
(CONVERT(DATE, TASK_START_DATE) >= #twoWeeksAgo
AND CONVERT(DATE, TASK_START_DATE) < #twoWeeksAhead)
OR (CONVERT(DATE, TASK_END_DATE) >= #twoWeeksAgo
AND CONVERT(DATE, TASK_END_DATE) < #twoWeeksAhead)

DATEADD with part days

I'm having a little trouble getting a count of dates in SQL SERVER. I require the number of calender days between 2 dates start and ends dates included. The problem with the example below is that it always returns 10 when I believe it should be 11.
DECLARE #FROM DATETIME, #TO DATETIME
SET #FROM = '18/12/2011 00:00:00'
SET #TO = '28/12/2011 00:00:00'
SELECT
DATEDIFF(MINUTE,#FROM,#TO), -- Returns 14459
DATEDIFF(HOUR,#FROM,#TO), -- Returns 241
DATEDIFF(DAY,#FROM,#TO), -- Returns 10
CEILING(CAST((DATEDIFF(HOUR,#FROM,#TO) / 24) as DECIMAL(9,5))) --Returns 10
CEILING(CAST(CEILING(CEILING(CAST(DATEDIFF(SECOND,#FROM,#TO) as DECIMAL(18,5))) / 60) / 60 as DECIMAL(9,5)) / 24) --Returns 10
The bottom line works if there is at least 1 second between the times but I must account for all scenarios.
My only other thought was to simply add one to the date diff to account for the part days? Is that reliable?
DATEDIFF(DAY,#FROM,#TO) + 1
I came across when answering this question How to find the total between the dates for each values
Is an expression that can be resolved to a time, date, smalldatetime,
datetime, datetime2, or datetimeoffset value. date can be an
expression, column expression, user-defined variable or string
literal. startdate is subtracted from end date.
This is taken from MSDN here.
28-18 = 10. I think you will always have to add 1 in the scenario you have because of the definition for DATEDIFF.
You need to set the #TO date to:
SET #TO = '28/12/2011 23:59:59'
To get the number of days between two dates (ignoring the time of day), including the start and end date, try;
SELECT FLOOR(CONVERT(FLOAT, #TO))-FLOOR(CONVERT(FLOAT, #FROM))+1
Edit:
SELECT DATEDIFF(d, #FROM, #TO)+1
seems to return the exact same results, which would indeed make it a more elegant way of doing it. Always thought DATEDIFF timeparts were about truncating after the calculation (which would give the wrong result if the start time was later in the day than the end time) and not truncating before the calculation which gives the correct result for your case. You learn something new every day :)
If you want a close equivalent of the C# DateTime.TotalDays() function (i.e. to know fractional days) you can use the following:
DECLARE #start DATETIME = '10 Apr 2012 15:00'
DECLARE #end DATETIME = '12 Apr 2012 16:00'
SELECT CONVERT(FLOAT, DATEDIFF(SECOND, #start, #end)) / 86400
*Note: 86400 = seconds in a day = 24 hours x 60 mins x 60 seconds

Resources