I'm looking to calculate how many weeks an employee will have worked if they started mid year until the end of the current fiscal year (1st April - 31st March).
For example, an employee started working at the company on 01/10/2017 (UK date) I need to calculate the number of weeks they will have worked until 31/03/2018 (inclusive).
The field for the employee start date is 'START_DATE' from table 'Employee'. I also have a calendar table with every date format you could imagine and also includes fiscal year.
I found this question but it doesn't quite solve my problem: Calculate totals of field based on current fiscal year only - SQL
Any help much appreciated.
It does depend on how you classify what a week is. Does it have to be a full week? Does starting on a day that is not a Monday mean that it's not counted as a full week if they finish on a day that is not Friday? This is where you have to identify your business logic.
Here are some fundamental DATEDIFF operations that you can use to work out differences between two dates, which you can use as a basis for your calculations:
DECLARE #startDate DATE = '2017-10-01'
DECLARE #endDate DATE = '2018-03-31'
SELECT #startDate StartDate, #endDate EndDate,
DATEDIFF(DAY, #startDate, #endDate) DaysPassed,
DATEDIFF(WEEK, #startDate, #endDate) WeeksWorked,
DATEDIFF(DAY, #startDate, #endDate) / 7.0 CalculatedWeeksWorked
Produces:
StartDate EndDate DaysPassed WeeksWorked CalculatedWeeksWorked
---------- ---------- ----------- ----------- ---------------------
2017-10-01 2018-03-31 181 25 25.857142
Also, you may want to consider the number of days worked excluding weekends to work out how many full weeks are worked, if so, have a look at this post:
get DATEDIFF excluding weekends using sql server
Fiscal Year
To work out the fiscal year, you should be able to simply look at the month value of the date like so:
DECLARE #startDate DATE = '2017-10-01';
-- if the month is greater than 3, add a year, else take the current year
SELECT CASE
WHEN DATEPART(MONTH, #startDate) > 3 THEN
CAST(DATEPART(YEAR, #startDate) + 1 AS VARCHAR(10)) + '-03-31'
ELSE
CAST(DATEPART(YEAR, #startDate) AS VARCHAR(10)) + '-03-31'
END AS fiscalYearEnd;
Edit the #startDate and test the above, it should hopefully work for most cases. I've given it a quick test and it seems to return the expected result.
DATEDIFF function:
ROUND(DATEDIFF(CURRENT_TIMESTAMP, START_DATE)/7, 0) AS weeks
where 0 is the number of decimal.
The problem with WEEKS is that it won't return correct results for dates that cross over January 1st.
Related
I have a table of names and associated birthdates. I can retrieve the names of everyone whose birthday is today by matching the MONTH and DAY dateparts between the birthdate in the database and the current date. However, I need to pull "looking forward" lists of, say, all birthdays in the next two weeks.
The obvious solution would be a computed column for each person, showing his/her "birthday this year". It's easy to pull the month and day from the birthday, add the current year, and cast the whole string as a date. That way I could just retrieve those whose "birthday this year" is within X days of the current date. However, I have one person with a birthday on February 29, and there isn't a Feb. 29 every year, so the calculated column "chokes" when I query or open the table with the following error:
The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value.
Suggestions on a way to either make a computed column work properly in this situation, or an alternative way to query using the date of birth in the table?
Change your query from using convert to using try_convert which will not error on nonexistent date but will instead return NULL. This will exclude your Feb 29 birthday client if running year is not leap.
Your approach won't work for the end of December, because the year changes. Here is a different approach:
Add the number of years to the date and let the database handle leap years.
Then do the comparison.
So, the logic for the first is:
select dateadd(year, year(getdate()) - year(dob), dob)
Then to get dates of birth in the next two weeks:
where dateadd(year, year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
However, this still doesn't handle dates of birth that could be next year. So, to handle the end of year, consider that as well:
where (dateadd(year, year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
) or
(dateadd(year, 1 + year(getdate()) - year(dob), dob) >= convert(date, getdate()) and
dateadd(year, 1 + year(getdate()) - year(dob), dob) < dateadd(14, day, convert(date, getdate())
)
To do this - you need to calculate the current year's DOB and the persons next date of birth. To calculate the current year date of birth we can use a simple calculation:
dateadd(year, datediff(year, DateOfBirth, CurrentDate), DateOfBirth)
Then - we need to calculate the next date of birth, which is simply adding a year if the current year DOB is less than CurrentDate:
dateadd(year, iif(CurrentDOB < CurrentDate, 1, 0), CurrentDOB)
Now - it is just a matter of checking if the next DOB is in the range. Here is some sample data to show how to put this together.
--==== Some sample dates of birth - including leap year DOB's
Declare #testData Table (DateOfBirth date);
Insert Into #testData (DateOfBirth)
Values ('1992-01-09'), ('2020-02-29'), ('1965-09-30'), ('1984-02-29');
--==== Test using different dates
Declare #current_date date = '2021-02-28';
--==== Use CROSS APPLY to calculate current year DOB and Next DOB
Select *
From #testData As td
Cross Apply (Values (dateadd(year, datediff(year, td.DateOfBirth, #current_date), td.DateOfBirth))) As y(CurrentDOB)
Cross Apply (Values (dateadd(year, iif(y.CurrentDOB < #current_date, 1, 0), y.CurrentDOB))) As n(NextDOB)
Where n.NextDOB <= dateadd(day, 14, #current_date);
If today is 2021-02-28 then the birthdays that fall on the 29th will be included. If today is 2021-03-01 then those will not be included because they would be calculated as 2022-02-28 which is not within 14 days.
I'm trying to get listed the corresponding week number from the dates that result from this query.
select Date,Time,EndDate,EndTime
FROM Test
WHERE (StartDate >= '01.01.2019')
ORDER BY StartDate
Basically, I want adding to the end column the week number from this query.
I can't edit the database in anyway, I just want to extract the week number from the dates and have it as a column at the end of my results.
Sample data below:
Just use datepart function:
select datepart(week, Date), Date,Time,EndDate,EndTime
FROM Test
WHERE (StartDate >= '01.01.2019')
ORDER BY StartDate
use datepart(wk,date):-
select Date,Time,EndDate,EndTime,datepart(wk,date)as week
FROM Test
WHERE (StartDate >= '01.01.2019')
ORDER BY StartDate
In UK ISO week is used: the first year's week is the one including 4th of Jan.
So:
set datefirst 1 --this sets Monday as first day of the week
set dateformat dmy -- nosrmal date format
select Date,Time,EndDate,EndTime,datepart(iso_week,date)as week
FROM Test
WHERE (StartDate >= '01.01.2019')
ORDER BY StartDate
Remember that first days of Jan may be 52nd or 53rd week of previous year and also last day of December may belong to first week of new year.
the check to see the week number and postponed to the yeear it belongs to is the following:
week_and_year = case when datepart(iso_week,date)>=52 and month(date)=1
then concat(year(date)-1,datepart(iso_week,date))
when datepart(iso_week,date)=1 and month(date)=12
then concat(year(date)+1,datepart(iso_week,date))
else concat(year(date),datepart(iso_week,date))
end
SELECT DATEPART(WEEK,GETDATE()-14)
SELECT DATEPART(WEEK,GETDATE()-7)
SELECT DATEPART(WEEK,GETDATE())
SELECT DATEPART(WEEK,GETDATE()+7)
SELECT DATEPART(WEEK,GETDATE()+14)
If you use Vertica, try this
date_part('ISODOW', date)
'ISODOW' - The ISO day of the week, an integer between 1 and 7 where Monday is 1.
I usually use the ROW_NUMBER() function to accomplish this:
select
Date,
Time,
EndDate,
EndTime,
ROW_NUMBER() over (partition by year(EndDate), datepart(weekday, EndDate) order by EndDate) as WeekNumInYear
FROM Test
WHERE
(StartDate >= '01.01.2019')
ORDER BY
StartDate
Can someone suggest me how do we consider week to start on Sunday and end on Saturday, while numbering them backwards in a 52 week rolling report like week1, week2.. week52
I want to count my current week as Week1 starting on Sunday, so even if its partial week its still week1 and last week Sunday-Saturday is week2 and so on until 52nd week last year (that would roughly be in September counting backwards). I need this as I am working on a daily report that will look for sales for current week and past 51 (full) weeks. My report should also return any week without sales '0' without skipping it.
Here is a way. Note I created the recursive CTE to populate some dates. You won't have to do this step, and real only need the YourWeekOrder = ... part.
declare #startDate date = dateadd(year,-1,getdate())
declare #endDate date = getdate()
;with cte as(
select #startDate as TheDate
union all
select dateadd(day,1,TheDate)
from cte
where TheDate < #endDate)
select
TheDate
,TheWeekOfYear = datepart(week,TheDate)
,YourWeekOrder = dense_rank() over (order by cast(datepart(year,TheDate) as char(4)) + case when len(datepart(week,TheDate)) = 1 then '0' + cast(datepart(week,TheDate) as char(2)) else cast(datepart(week,TheDate) as char(2)) end desc)
from cte
order by
TheDate
option(maxrecursion 0)
SEE IT IN ACTION HERE
i need help on my little problem.
SELECT FORMAT(ServiceDate, 'dd-MM-yyy") AS ServiceDate
FROM Services
WHERE Day(ServiceDate) BETWEEN '1' AND Day(getdate() -2)
AND Month(ServiceDate) =
CASE
WHEN Day(getdate()) <=2
THEN Month(getdate() -1
ELSE Month(getdate())
END
AND Year(ServiceDate) = Year(getdate())
Now the problem is the first and the second of the Month.
The query don't use the last month. It shows the actual month.
I hope its clear what i need.
if we have the 01-06-2016 and i need minus 2, so the query must give me back to the day 30-05-2016
big THX
the output for today with this query
output query
Assuming you are using sql-server, you need to use DATEADD(Day, -2, GETDATE()) for subtracting 2 days from current date.
I think I understand the logic now:
If the current day is the 1st of the month, get all the records from the start of previous month, until 2 days before it ends.
If the current day is the 2nd of the month, get all the records from the start of the previous month until one day before it ends.
If the current day is the 3rd of the month or higher, get all the records from the beginning of the current month until 2 days ago.
Since you are using the FORMAT() function that was introduced in 2012 version, you can also use the EOMONTH() function that was introduced in the same version.
This function returns the date of the end of the month of the date it receives as an argument, and also have a useful optional second argument that specifies the numbers of months to add to the date passed to the function.
Using this function will allow you to write your query without using any functions on the ServiceDate column, thus enabling the use of any indexes defined on this column.
DECLARE #Now datetime = GETDATE()
SELECT FORMAT(ServiceDate, 'dd-MM-yyy') AS ServiceDate
FROM Services
WHERE (
DAY(#Now) <= 2
AND ServiceDate >= DATEADD(DAY, 1, EOMONTH(#Now, -2))
AND ServiceDate < DATEADD(DAY, -(DAY(#Now)-1), EOMONTH(#Now, -1))
)
OR
(
DAY(GETDATE()) > 2
AND ServiceDate >= DATEADD(DAY, 1, EOMONTH(#Now, -1))
AND ServiceDate < DATEADD(DAY, -2, #Now)
)
Compute enddate as 2 days before getdate() and select data in interval from enddate's first of month and enddate.
SELECT FORMAT(ServiceDate, 'dd-MM-yyy") AS ServiceDate
FROM Services
CROSS APPLY (SELECT enddate = DATEADD(D,-2,getdate()) x
WHERE ServiceDate BETWEEN DATEADD(MONTH,DATEDIFF(MONTH,0,x.enddate),0) AND x.enddate
I'm hoping to find a solution for this to automate a report I have. Basically what I'm trying to accomplish here is grabbing a date (first day of previous month, two years ago through last day of previous month current year).
So the date span if running this month would look like this: between 4/1/2013 and 3/31/2015
I have found code to get the date two years ago but I'm not able to also incorporate the month functions... Any help is very much appreciated!
For year I'm using this:
SELECT CONVERT(VARCHAR(25),DATEADD(year,-2,GETDATE()),101)
First day of previous month 2 years ago:
SELECT CONVERT(DATE,dateadd(day, -1, dateadd(day, 1 - day(GETDATE()), GETDATE())))
Last day of last month:
SELECT CONVERT(DATE,DATEADD(month, DATEDIFF(month, 0, DATEADD(year,-2,GETDATE())), 0))
Then just do whatever logic you need with them
Your where clause can look something like this:
where date >= cast(dateadd(year, -2,
dateadd(month, -1, getdate() - day(getdate()) + 1)
) as date) and
date < cast(getdate() - day(getdate()) + 1 as date)
This makes use of the handy convenience that subtracting/adding a number to a datetime is the same as adding a date. The start date says: get the first day of the month, then subtract one month, then subtract two years. This could have been done as dateadd(month, -25, . . .), but I think separating the logic is clearer.
This gives you two dates you are looking for:
SELECT
CAST((DATEADD(yy, -2, DATEADD(d, -1 * DATEPART(dd, getdate()) + 1 , GETDATE() ))) as date) as yourTwoYearsAgoDate,
CAST((DATEADD(d, -1 * DATEPART(dd, GETDATE()), GETDATE())) as date) as yourEndOfLastMonthDate
Given a reference date (e.g. "today"),
declare #today date = '23 April 2015'
The 1st of the month is computed by subtracting 1 less than the day number of the current month:
select first_of_current_month = dateadd(day,1-day(#today),#today)
The last day of the previous month is day 0 of the current month, so to get the last day of the previous month, just subtract the current day number:
select last_of_previous_month = dateadd(day,-day(#today),#today)
Moving two years back is easy:
select two_years_back = dateadd(year,-2, #today )
Putting it all together, this should do you:
declare #today date = '23 April 2015'
select *
first_day_of_current_month = dateadd(day,1-day(#today),#today),
last_day_of_previous_month = dateadd(day, -day(#today),#today) ,
date_from = dateadd(year,-2, dateadd(day,1-day(#today),#today) ) ,
date_thru = dateadd(day, -day(#today),#today)
yielding the expected results:
first_day_of_current_month: 2015-04-01
last_day_of_previous_month: 2015-03-31
date_from : 2013-04-01
date_thru : 2015-03-31
So you should be able to say something like this:
select *
from foo t
where t.transaction_date between dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and dateadd(day, -day(#today),#today)
If you have to deal with datetime values rather than date, its easier to not use between and say something like this:
declare #today date = current_timestamp -- get the current date without a time component
select *
from foo t
where t.transaction_date >= dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and t.transaction_date < dateadd(year, 0, dateadd(day, -day(#today),#today)
[superfluous addition of 0 years added for clarity]