How to get the exact year difference in SQL - sql

I need a simple way to find the exact year difference between two dates.
For example between 01.11.2013 and 30.10.2019. In this case, it should be 5 years because current date has not reached 01.11 yet. But if it is between 29.10.2013 and 30.10.2019 then it should be 6 years.
Let assume these are birthday and current days. It's similar.
I've tried to get the result with the following code:
select datediff(year,20131101,20191030)
It is giving me 6 instead of 5. The closest solution I've found is:
select datediff(day,20131101,20191030)/365
But as we know there could be 1 or 2 leap years in this period and the result is again not exactly what I expect. Neither when we divide with 365 nor 366.
It is not necessary using datediff. I wonder if there is some simple way to present it or some stored function to do this for which existing I did't know.

You can take the year difference and then subtract 1 if necessary:
select (datediff(year, val1, val2) -
(case when month(val1) < month(val2) then 0
when month(val1) > month(val2) then 1
when day(val1) <= day(val2) then 0
else 1
end)
) as diff_years

You can try datediff Month / 12.
Declare #date1 Datetime
Declare #date2 Datetime
set #date1='2013-11-01 23:59:59'
set #date2='2019-10-30 00:00:00'
(select (datediff(HOUR, #date1, #date2)/24/365 ) as diff_years)

Related

SSRS. Workday Function

I'm trying to convert the below Excel formula into SSRS but having looked around I cannot seem to find a solution. I can calculate the number of working days between two dates but what I'm trying to do is add on a number of working days to a date. Essentially I don't have the 2nd date.
I guess it would be something along the lines of the DATEADD function?
=WORKDAY($A1,B$1)
Hope someone can help
Many thanks
Here is a tsql solution to add X Business Days to a date.
declare #calendar as table (theDate date, dayOfWeek varchar (10));
declare #startDate as date = '20170704';
declare #businessDaysToAdd as integer = 10;
insert into #calendar
select theDate
, datename(dw, theDate) dow
from
dbo.dateTable('20170701', '20170720') ;
with temp as (
select theDate
, dayOfWeek
, rank() over (order by theDate) theRank
from #calendar
where theDate > #startDate
and dayOfWeek not in ('Saturday', 'Sunday')
)
select * from temp
where theRank = #businessDaysToAdd;
Notes
dbo.DateTable is a table valued function that just happens to exist in the database I was using. In real life, you might have an actual calendar table of some sort.
This example does not include holidays.
This is only the start of the answer to the posted question. It only solves the problem of Essentially I don't have the 2nd date.
Type this into the expression for the textbox. (From SSRS 2008 Datediff for Working Days)
=(DateDiff(DateInterval.day,Parameters!STARTDATE.Value,Parameters!ENDDATE.Value)+1)
-(DateDiff(DateInterval.WeekOfYear,Parameters!STARTDATE.Value,Parameters!ENDDATE.Value)*2)
-(iif(Weekday(Parameters!STARTDATE.Value) = 7,1,0)
-(iif(Weekday(Parameters!ENDDATE.Value) = 6,1,0))-1)
Ok after much perseverance I managed to get what I wanted in both TSQL and SSRS. My objective was to measure Agent productivity so I didn’t want to count the weekend and this would be unfair. If a date fell on a weekend then I wanted it to jump to a Monday. Likewise if adding number of days onto a date went over a weekend in the future then I needed the incremented date to reflect this. For the end user (In SSRS) I wanted a leading edge (Like an Upside down triangle) so that if the date + number working days was in the future then set to NULL, showing a zero would look like no productivity which is incorrect.
First TSQL - My base query started with the following SO thread but after trying many of the options I was finding when the date fell on a Saturday or Sunday the solution did not work for me (I was unable to create functions due to permissions). However tweaking the below got me there and I dealt with Sunday specifically
Add business days to date in SQL without loops
SELECT
,DATEADD(WEEKDAY, (/*Your Working Days*//5)*7+(/*Your Working Days*/ % 5) +
(CASE WHEN DATEPART(WEEKDAY,/*Your Date*/) <>7 AND DATEPART(WEEKDAY,/*Your Date*/) + (/*Your Working Days*/ % 5) >5 THEN 2
WHEN DATEPART(WEEKDAY,/*Your Date*/) = 7 AND DATEPART(WEEKDAY,/*Your Date*/) + (/*Your Working Days*/ % 5) >5 THEN 1 ELSE 0 END), /*Your Date*/) AS [IncrementedDate]
FROM /*YourTable*/
Then for SSRS - The 2 key points here is that TSQL will divide as an integer if the source number is an integer so this needs to be handled in SSRS and secondly you need to set the first week day to Monday as part of the expression.
I put this expression into a Matrix with Date Created being my Row Group and Contact Working Days being my Column Group.
=DATEADD("W",(INT(ReportItems!ContactWorkingDays.Value/5))*7+(ReportItems!ContactWorkingDays.Value MOD 5) + IIF(DATEPART("W",ReportItems!DateCreated.Value,FirstDayOfWeek.Monday) <> 7 AND (DATEPART("W",ReportItems!DateCreated.Value,FirstDayOfWeek.Monday) + (ReportItems!ContactWorkingDays.Value MOD 5) >5),2,IIF(DATEPART("W",ReportItems!DateCreated.Value,FirstDayOfWeek.Monday) = 7 AND (DATEPART("W",ReportItems!DateCreated.Value,FirstDayOfWeek.Monday) + (ReportItems!ContactWorkingDays.Value MOD 5) >5),1,0)),ReportItems!DateCreated.Value)
This does not include holidays - I'm not too bothered at this stage and that is for a rainy day! :)

Get the actual month difference between two date

I working with strange queries in SQL, I want to find the actual month difference between two dates, for example
StartDate = '1-1-2013'
EndDate = '4-30-2013'
Here I want result to be 4 not 3
select datediff(mm, '1-1-2013', '4-30-2013')
This query will provide me result 3 but actual result is 4. Can anyone help me on this?
Not quite sure if this helps but here's my stab at it:
declare
#date1 datetime = '2013-01-01',
#date2 datetime = '2013-04-30'
select Cast(Round(Cast(DATEDIFF(DD,#date1, #date2) as decimal(5,2)) / 30, 1) as int) as Months
What you're asking isn't as simple as it first appears. One obvious way would be to add one day to your end date before doing the datediff. This would then be doing 1 Jan to 1 May which would be reported as four months.
However you need to consider if there's any tolerances; for example, should 1 Jan to 29 Apr be considered four months, or is that still three months? Obviously this will depend heavily on the context in which you are using these dates.
Please try to add 01-01-2013 and 04-30-2013 instead of what you added here
So query will be as below
select datediff(mm, '1-1-2013', '4-30-2013')
Thanks
Vasanthan
As others have pointed out, the result is absolutely correct: 3 months and 29 days.
You can of course check if the date in the end date is the last date of the month and make an adjustment to produce the desired result:
DECLARE #START DATETIME, #END DATETIME
SELECT #START = '2014-01-01', #END = '2014-04-30'
SELECT #START, #END
SELECT DATEDIFF(MONTH, #START, CASE WHEN MONTH(DATEADD(DAY, 1, #END)) > MONTH(#END) THEN DATEADD(DAY, +1, #END) ELSE #END END)
Not sure about the performance of said example (you're free to experiment with similar ideas), but what it does is add the last missing day to produce the 4 month difference whenever the end date represents the last day of that month.
Now, I agree with the others here that this is a weird thing to do, but if this is what your case requires, then so be it. We can't really comment on your business / programming specific needs here. :)

How to get every Monday out of a Date

I need some help to understand a certain line in a code. The code takes the turnovers of every Monday in the year 2010 and at the last line in summarizes all the turnovers to one.
Here is the code:
SELECT
CASE
WHEN GROUPING (DATUM) = 1 THEN 'Gesamtumsatz'
ELSE CAST (DATUM AS VARCHAR (40))
END AS MONTAGSDATEN,
AVG (VERKAUFSWERT * VERKAUFSMENGE) as UMSATZ
FROM Data_Star_Awesome.dbo.VERKAUFSFAKTEN vk
INNER JOIN DIMDATUM dimD on vk.DATUMID=dimD.DATUMID
WHERE DATEDIFF(dd,0, DATUM)%7=0
AND JAHR = 2010
GROUP BY ROLLUP (DATUM)
The problematic line I don't understand is the following:
WHERE DATEDIFF(dd,0, DATUM)%7=0
What I know is that it takes the days out of the date variable but I don't get the %7=0 part. The DATEDIFF function should give back all the days. Are these days saved in the % the placeholder? And how does it get all the Mondays by using the 7=0?
It would be great if someone could help me out.
Thanks a lot :)
Modulo or % operator is the same as in a lot of programming languages. It returns the remainder after the division.
The DATEDIFF function takes two dates and returns the difference in a specified datepart, which in your query is the days represented by dd.
Also 0 as date converts to 1/1/1900 which happens to be a Monday. So your query is calculating the days between 1/1/1900 and the DATUM field in days and if its Mod is 0 then DATUM is Monday.
You could simply say:
datename(weekday,<datetime-value>)
Which will return 'Monday', 'Tuesday', 'Wednesday', etc. The problem with this approach is that the returned value is localized. If the SQL server's language is changed, your test for 'Monday' will fail.
This expression will always work:
( ##datefirst + ( datepart(weekday,today) - 1 ) ) % 7
It evaluates to 1-7, where Monday is always 1 and Sunday is always 7, regardless of culture/language settings or the current value of ##datefirst, as set by set datefirst.
We can then convert this into this discriminant function, yielding 1 or 0 to indicate whether or not the date is Monday:
case ( ##datefirst + ( datepart(weekday,today) - 1 ) ) % 7 when 1 then 1 else 0 end

Month difference between two dates in sql server

Please refer the below examples and kindly let me know your ideas.
declare #EmployeeStartDate datetime='01-Sep-2013'
declare #EmployeeEndDate datetime='15-Nov-2013'
select DateDiff(mm,#EmployeeStartDate, DateAdd(mm, 1,#EmployeeEndDate))
Output = 3
expected output = 2.5
Since I have only 15 days in Nov, So I should get 0.5 for Nov
Try this
SELECT CASE WHEN DATEDIFF(d,'2013-09-01', '2013-11-15')>30 THEN DATEDIFF(d,'2013-09-01', '2013-11-15')/30.0 ELSE 0 END AS 'MonthDifference'
OR
SELECT DATEDIFF(DAY, '2013-09-01', '2013-11-15') / 30.436875E
DateDiff compares the values of the column you specify to work out the difference, it doesn't compare both dates and give you an exact difference. You've told it to compare the Month values, so thats all it's looking it.
http://technet.microsoft.com/en-us/library/ms189794.aspx
The Technet article details the return value of the DateDiff Function - note that it's only int.
If you want the value as an exact figure (or nearabouts), you should datediff the dates on days, then divide by 30. For neatness, I've also rounded to a single decimal place.
select Round(Convert(decimal, DateDiff(dd,#EmployeeStartDate, #EmployeeEndDate)) / 30, 1)
select CAST(DATEDIFF(MONTH, StartDate, EndDate) AS float) -
(DATEPART(dd,StartDate)*1.0 - 1.0) / DAY(EOMONTH(StartDate)) +
((DATEPART(dd,EndDate)*1.0 ) / DAY(EOMONTH(EndDate)))
Here you go:
declare #EmployeeStartDate datetime='01-Sep-2013'
declare #EmployeeEndDate datetime='15-Nov-2013'
;WITH cDayDiff AS
(
select DateDiff(dd,#EmployeeStartDate, DateAdd(dd, 1,#EmployeeEndDate)) as days
)
SELECT
CAST(days as float) / 30 as Months
FROM
cDayDiff
It has 76 days which equals to 2.5333
Output:
Months
============
2.53333333333333
As far as I can tell, none of the other answers take into account that not all months are exactly 30 days long.
This is what I came up with:
DECLARE #StartDate DATETIME = '07-Oct-2018'
DECLARE #EndDate DATETIME = '06-Nov-2018'
SELECT
DATEDIFF(m, #StartDate, #EndDate)
+ 1.0 * DAY(#EndDate) / DAY(EOMONTH(#EndDate))
- 1.0 * DAY(#StartDate) / DAY(EOMONTH(#StartDate))
-- 0.974193548388
The formula can be explained like this:
The difference in months as output by DATEDIFF(m, ..., ...)
Plus the day of the last date divided by the number of days in that month
Minus the day of the first date divided by the number of days in that month
Note that in this case the answer from Ren Yuzhi gives the result 1.006451612904. The 1.0 is necessary to make the division happen in floating point rather than integer.
declare #EmployeeStartDate datetime='013-09-01'
declare #EmployeeEndDate datetime='2013-11-15'
SELECT DATEDIFF(month, #EmployeeStartDate, #EmployeeEndDate)
Thanks for this.
https://www.w3schools.com/sql/func_sqlserver_datediff.asp
You can use Below to calculate the No Of Months between two Dates in MySQL,
PERIOD_DIFF(concat(year(Date1),LPAD(month(Date1),2,0)),concat(year(Date2),LPAD(month(Date2),2,0)))

Sliding Week Comparison

I want to compare data from the current week (1 through 52) to the data from two weeks prior. I need to make sure that when the new-year boundary is crossed, the comparison keeps working. I have a working predicate, like this:
WHERE ( DATEPART(ww,MA.DateOpen) + 1 ) % 52 = DATEPART(ww,#ImportDate) - 1
The above logic works, but it does not account for proximate years. In other words, unwanted data from week 'X' of all prior years will be included in the comparison. So the predicate must be expanded to account for years, which in turn requires handling the new year boundary: week 1 of 2012 should be compared against week 51 of 2011.
How can this be done without a verbose looking predicate?
update:
This question is complicated by the need for "week of year" to be treated as Tuesday through Tuesday. My current "correct" attempt is embodied in the [VerboseCheck] case below.
SET DATEFIRST 2
DECLARE #ImportDate date = '2011-1-4'
DECLARE #DateOpen date = '2010-12-20'
select #ImportDate,
YEAR(#DateOpen),
DATEPART(ww, #DateOpen),
YEAR(DATEADD(dd,-14,#ImportDate)),
DATEPART(ww,DATEADD(dd,-14,#ImportDate)),
CASE WHEN
YEAR(#DateOpen) + DATEPART(ww, #DateOpen) =
YEAR(DATEADD(dd,-14,#ImportDate)) + DATEPART(ww,DATEADD(dd,-14,#ImportDate))
THEN 1 ELSE 0 END [VerboseCheck],
CASE WHEN DATEDIFF (ww, #DateOpen, #ImportDate) =2
THEN 1 ELSE 0 END [SimpleCheck]
The [SimpleCheck] would be perfect, but it gives a different answer than [VerboseCheck]. If I advance #DateOpen one more day, then the two give the same answer. This suggests that both are not honoring the DATEFIRST evaluation in the same way.
Seems like DateDiff would be best used here. However as you noted DATEFIRST is indeed not respected.
So we just add Itzik Ben-Gan's solution and we get
WHERE DATEDIFF (ww,
DATEADD( day, -##DATEFIRST , #DateOpen),
DATEADD( day, -##DATEFIRST , #ImportDate)
) =2
why not just DATEADD('dd', -14, DateOpen)?
Would something like the following work to compare the years?
WHERE ( DATEPART(ww,MA.DateOpen) + 1 ) % 52 = DATEPART(ww,#ImportDate) - 1
AND YEAR(MA.DateOpen)=YEAR(DATEADD(d, -14, #ImportDate))