SQL Date range using dynamic params - sql-server

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()

Related

The datediff function resulted in an overflow message

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

T-SQL Count days between two days (datediff not quite working)

We have a requirement to bill our customers per day. We bill for an asset's existence in our system on that day. So, I started with datediff...
select datediff(dd ,'2015-04-24 12:59:32.050' ,'2015-05-01 00:59:59.000');
Returns this:
7
But I need to count the following dates: 4/24,4/25,4/26,4/27,4/28,4/29, 4/30, 5/1, which are 8 days. So datediff isn't quite working right. I tried these variations below
--too simple, returns 7, i need it to return 8
select datediff(dd ,'2015-04-24 12:59:32.050', '2015-05-01 23:59:59.000');
--looking better, this returns the 8 i need
select ceiling(datediff(hh,'2015-04-24 12:59:32.050', '2015-05-01 23:59:59.000')/24.0);
-- returns 7, even though the answer still needs to be 8. (changed enddate)
select ceiling(datediff(hh,'2015-04-24 12:59:32.050', '2015-05-01 00:59:59.000')/24.0);
So, my question... How, in SQL, would I derive the date count like i described, since I believe datediff counts the number of day boundaries crossed.... My current best approach is loop through each day in a cursor and count. Ick.
Use CONVERT to get rid of the time part, add 1 to get the desired result:
SELECT DATEDIFF(dd,
CONVERT(DATE, '2015-04-24 12:59:32.050'),
CONVERT(DATE, '2015-05-01 00:59:59.000')) + 1;
It turns out the time part does not play any significant role in DATEDIFF when dd is used as the datepart argument. Hence, CONVERT is redundant. This:
SELECT DATEDIFF(dd, '2015-04-24 23:59:59.59','2015-05-01 00:00:00.000') + 1
will return 8 as well.
You could try this which would return 8 days.
select datediff(dd ,'2015-04-24 12:59:32.050' ,CASE DATEDIFF(Second,'2015-05-01 00:00:00.000','2015-05-01 23:59:59.000') WHEN 0 THEN '2015-05-01 23:59:59.000' ELSE DATEADD(dd,+1,'2015-05-01 23:59:59.000') END)
If you want to use variables for your dates then something like this would work.
BEGIN
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
DECLARE #EndDateOnly DATE
SET #StartDate = '2015-04-24 12:59:32.050'
SET #EndDate = '2015-05-01 23:59:59.000'
SET #EndDateOnly = CAST(#EndDate AS DATE)
SELECT datediff(dd ,#StartDate ,CASE DATEDIFF(Second,CAST(#EndDateOnly||' 00:00:00.000' AS DATETIME),#EndDate) WHEN 0 THEN #EndDate ELSE DATEADD(dd,+1,#EndDate) END)
END

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'))

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)

Deterministic scalar function to get day of week for a date

SQL Server, trying to get day of week via a deterministic UDF.
Im sure this must be possible, but cant figure it out.
UPDATE: SAMPLE CODE..
CREATE VIEW V_Stuff WITH SCHEMABINDING AS
SELECT
MD.ID,
MD.[DateTime]
...
dbo.FN_DayNumeric_DateTime(MD.DateTime) AS [Day],
dbo.FN_TimeNumeric_DateTime(MD.DateTime) AS [Time],
...
FROM {SOMEWHERE}
GO
CREATE UNIQUE CLUSTERED INDEX V_Stuff_Index ON V_Stuff (ID, [DateTime])
GO
Ok, i figured it..
CREATE FUNCTION [dbo].[FN_DayNumeric_DateTime]
(#DT DateTime)
RETURNS INT WITH SCHEMABINDING
AS
BEGIN
DECLARE #Result int
DECLARE #FIRST_DATE DATETIME
SELECT #FIRST_DATE = convert(DATETIME,-53690+((7+5)%7),112)
SET #Result = datediff(dd,dateadd(dd,(datediff(dd,#FIRST_DATE,#DT)/7)*7,#FIRST_DATE), #DT)
RETURN (#Result)
END
GO
Slightly similar approach to aforementioned solution, but just a one-liner that could be used inside a function or inline for computed column.
Assumptions:
You don't have dates before
1899-12-31 (which is a Sunday)
You want to imitate ##datefirst = 7
#dt is smalldatetime, datetime,
date, or datetime2 data type
If you'd rather it be different, change the date '18991231' to a date with the weekday that you'd like to equal 1. The convert() function is key to making the whole thing work - cast does NOT do the trick:
((datediff(day, convert(datetime,
'18991231', 112), #dt) % 7)
+ 1)
I know this post is way-super-old, but I was trying to do a similar thing and came up with a different solution and figured I'd post for posterity. Plus I did some searching around and did not find much content on this question.
In my case, I was trying to use a computed column PERSISTED, which requires the calculation to be deterministic. The calculation I used is:
datediff(dd,'2010-01-03',[DateColumn]) % 7 + 1
The idea is to figure out a known Sunday that you know will occur before any possible date in your table (in this case, Jan 3 2010), then calculate the modulo 7 + 1 of the number of days since that Sunday.
The problem is that including a literal date in the function call is enough to mark it as non-deterministic. You can work around that by using the integer 0 to represent the epoch, which for SQL Server is Jan 1st, 1900, a Sunday.
datediff(dd,0,[DateColumn]) % 7 + 1
The +1 just makes the result work the same as datepart(dw,[datecolumn]) when datefirst is set to 7 (default for US), which sets Sunday to 1, Monday to 2, etc
I can also use this in conjunction with case [thatComputedColumn] when 1 then 'Sunday' when 2 then 'Monday' ... etc. Wordier, but deterministic, which was a requirement in my environs.
Taken from Deterministic scalar function to get week of year for a date
;
with
Dates(DateValue) as
(
select cast('2000-01-01' as date)
union all
select dateadd(day, 1, DateValue) from Dates where DateValue < '2050-01-01'
)
select
year(DateValue) * 10000 + month(DateValue) * 100 + day(DateValue) as DateKey, DateValue,
datediff(day, dateadd(week, datediff(week, 0, DateValue), 0), DateValue) + 2 as DayOfWeek,
datediff(week, dateadd(month, datediff(month, 0, DateValue), 0), DateValue) + 1 as WeekOfMonth,
datediff(week, dateadd(year, datediff(year, 0, DateValue), 0), DateValue) + 1 as WeekOfYear
from Dates option (maxrecursion 0)
There is an already built-in function in sql to do it:
SELECT DATEPART(weekday, '2009-11-11')
EDIT:
If you really need deterministic UDF:
CREATE FUNCTION DayOfWeek(#myDate DATETIME )
RETURNS int
AS
BEGIN
RETURN DATEPART(weekday, #myDate)
END
GO
SELECT dbo.DayOfWeek('2009-11-11')
EDIT again: this is actually wrong, as DATEPART(weekday) is not deterministic.
UPDATE:
DATEPART(weekday) is non-deterministic because it relies on DATEFIRST (source).
You can change it with SET DATEFIRST but you can't call it inside a stored function.
I think the next step is to make your own implementation, using your preferred DATEFIRST inside it (and not considering it at all, using for example Monday as first day).
The proposed solution has one problem - it returns 0 for Saturdays. Assuming that we're looking for something compatible with DATEPART(WEEKDAY) this is an issue.
Nothing a simple CASE statement won't fix, though.
Make a function, and have #dbdate varchar(8) as your input variable.
Have it return the following:
RETURN (DATEDIFF(dd, -1, convert(datetime, #dbdate, 112)) % 7)+1;
The value 112 is the sql style YYYYMMDD.
This is deterministic because the datediff does not receive a string input, if it were to receive a string it would no longer work because it internally converts it to a datetime object. Which is not deterministic.
Not sure what you are looking for, but if this is part of a website, try this php function from http://php.net/manual/en/function.date.php
function weekday($fyear, $fmonth, $fday) //0 is monday
{
return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7;
}
The day of the week? Why don't you just use DATEPART?
DATEPART(weekday, YEAR_DATE)
Can't you just select it with something like:
SELECT DATENAME(dw, GETDATE());

Resources