SSRS. Workday Function - sql

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! :)

Related

Two columns with dates (one any day of week, another one WEEK ENDING DATE (SATURDAY) BASED ON 1st column)

I have such a situation.
I need to have 2 columns 1) Is just pull data from a table (just as it is) r.[RCLDTE] (Day of week)
and 2 column) I need to basically look at the first column and make it Saturday of that week.
SELECT r.[RCLDTE] AS 'Day of Week'
,r.[RCLDTE] AS 'Week Ending Day (Saturday)'
Before what I was doing at similar projects I just used this code and added to WHERE statement.
WHERE CONVERT(DATE, CONVERT(CHAR(8), r.[RCLDTE] )) = cast(DATEADD(dd, DATEPART(DW,GETDATE())*-1, GETDATE()) as date)
This code was changing the dates column to Saturday.
However, here I have a different situation. I need 2 columns 1) as it is and 2) where dates will be Saturdays of the week from r.[RCLDTE] column , as a result from the way how I understand I cannot use WHERE statement because it will affect both columns.
Does someone know how I can leave 1st column as it is and 2nd a column of Saturday.
Please let me know.
Thanks.
To avoid issues when someone changes either DATEFIRST or LANGUAGE settings, you can use this. Also, given that you are storing dates in a numeric column for some reason (you really should provide feedback to whoever owns the system so they can fix it), we have to first try to convert those values to a date (they may not all be valid, which is one of the problems with using the wrong data type):
;WITH t AS
(
SELECT *, ProperDate = CASE WHEN ISDATE(CONVERT(char(8), RCLDTE)) = 1
THEN CONVERT(date, CONVERT(char(8), RCLDTE)) END
FROM dbo.tablename
)
SELECT [Language] = ##language, [Datefirst] = ##datefirst,
RCLDTE = CASE WHEN ProperDate IS NULL THEN RCLDTE END,
[Day of Week] = ProperDate,
[Saturday] = DATEADD
(
DAY,
6 - ((DATEPART(WEEKDAY, ProperDate) + ##DATEFIRST - 1) % 7),
ProperDate
)
FROM t;
Updated db<>fiddle that also demonstrates the handling of garbage data and a version of SQL Server so old that TRY_CONVERT() didn't exist yet (at least 12 years ago).
here is one way :
select
r.RCLDTE AS 'Day of Week'
, dateadd(day, 7 - datepart(weekday, r.RCLDTE) , r.RCLDTE)
from tablename r
db<>fiddle here

How does this aggregated query find the correct values

I am having trouble understanding this code. It actually seems to work, but I don't understand how the correct value for activity year and month is "found" get the proper min and max? Or is it running all permutations getting the highest? This is very strange to me.
I do understand how the dateadd works, just not how the query is actually working on the whole. This may be a bad question since I don't actually need help solving a problem, just insight into why this works.
select
EmployeeNumber,
sum(BaseCalculation) as BaseCalculation,
min(dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)) as StartDate,
max(dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)) as EndDate
from
Compensation
where
1=1
-- and
group by
EmployeeNumber
for both the min and max function call, the algorithm is
dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)
Your query compute all possible date from the Compensation table using this algorithm. Then, you select the minimum date as StartDate and the maximum as EndDate.
This is how the proper max and min are returned.
Note that the dateadd signature is DATEADD (datepart , number , date )
Since the last parameter is 0, you are addind to month(mm) the number calculated in the algorithm, and return the corresponding date starting from 0.
Check this out for more information : https://msdn.microsoft.com/en-us/library/ms186819.aspx
It is converting the columns ActivityYear and ActivityMmonth to a date. It is doing so by counting the number of months since 1900 and adding them to time zero. So, Jan 2000 would become something like Jan, 100. This seems like a very arcane calculation, because dates that are about 2,000 years old are not really useful.
Of course, this assumes that ActivityYear is a recognizable recent year.
I would convert the year and month to the first day of the beginning of the month, with something like this:
min(cast(cast(ActivityYear * 10000 + ActivityMonth + 1 as varchar(255)) as date)
Sql Server will calculate every value of that statement, and then only return the min and max.
Although I cannot say for sure that sql server executes this way internally, the way I think about it I imagine that the engine strips off the group by and all the aggregate functions, runs that query. And then just sums/finds the min etc off of that.

SQL check if contracts are active in each month of the quarter (year). Sum values/(duration in mths)

I am working with an excel file that contains data and a vba to filter the data via ADO using an SQL string.
My database contains:
Surname; Start_date (of the contract), Dur_mths (contract duration in months), Value (Total value of the contract).
A contract may have the duration of 2-3 years. Therefore I would like to divide the total value of each contract by duration(Value/m), check if the contract is active in the specific period and then sum the results for each Surname. Each Surname may have several contracts active in a given period. I would like to see the sum of all contract values divided by their durations for each Surname for a given period.
My current working code for one month (January 2012) looks like this:
SELECT [Surname],
Sum (iif([Start_date] <= '2012-1-1'
AND DateAdd ("m",[Dur_mths], [Start_date]) >= DateSerial(2012,1+1, 0),[Value]/[Dur_mths],0))
AS [SumValue] FROM [Data$]
GROUP BY [Surname]
Now, I would like to make it work for a quarter (or a year).
I understand this still requires checking for each month in a quarter if the contract is active betw. start of contract and end of contract (start plus duration in months).
Here comes my question:
Is there an easy way to develop the code to do the same for a quarter (a year, any period)? Ie. to check if the contract is active for several months in a given period (quarter, year)? And then sum the values of active contracts(/duration in months) for each customer?
Thank you very much for all the help.
Jacek
EDIT! : In the case of one quarter, for one customer or surname, the calculation should test: in how many months in the quarter (or in the year, or in the specified period) the contract is active.
m1 m2 m3
contr1 50 50 0 (contract ended in m2)
contr2 0 0 20 (contract starts in m3)
contr3 10 10 10 (contract started 1y ego and will continue well into 2015) etc.
For this single surname, the sum for the quarter should give 150.
EDIT2:
I am now thinking how to perhaps use a loop to calculate how many months in a quarter (or a year, or a given period) the contract was active. Eg. I am trying to develop a loop, sopmething like (It is not actually any working code but an idea what I think needs to be done, I am not a programmer unfortunately):
(??? i do not know what to do with it yet...)
DECLARE #I INT, #N INT;
DECLARE #StartDate DATETIME #EndDate DATETIME
SET #I = 1
SET #N = 0
SET #QueryStartDate = 2012-01-01
SET #EndDate = DateAdd ("m",[Dur_mths], [Start_date])
WHILE #I <= 3
BEGIN
SET #N = #N + iif(DateSerial(year[Start_date];month[Start_date]+#I-1;1); <= #QueryStartDate AND #EndDate >= DateSerial(year(#StartDate),month(#StartDate+#I, 0),1,0))
SET #I = #I + 1
END
(??? i do not know what to do with it yet...)
It would return the number of months the contract was active - #N. I would use it to multiply my contract/(duration in months) in the initial code. I am trying now to read how to incorporate it in my initial code.
You can, perhaps, use the same DateAdd function with various parameters to get the data for a quarter or a year, as below:
AND DateAdd ("q",[Dur_quarter], [Start_date]) >= DateSerial(2012,1+1, 0),[Value]/[Dur_quarter],0))
and
AND DateAdd ("yyyy",[Dur_year], [Start_date]) >= DateSerial(2012,1+1, 0),[Value]/[Dur_year],0))
The complete details of the DATEADD function are provided on MSDN here.
For the time being I arrived at a following solution for a single quarter -by simply repeating the code for the month. The date range and region is entered to sql string from excel cells...I am still wondering if it can be made somehow universal for any period (with a loop?) In addition I added a left join
SELECT [Data$].[Surname],[DataInternal$].[Code], [reg],
Sum (iif([Start_date]
<= '2012-1-1'
AND DateAdd ("m",[Dur_mths], [Start_date])
>= DateSerial(2012,1+1, 0)
,[Value]/[Dur_mths],0)) AS [M1],
Sum (iif([Start_date]
<= '2012-2-1'
AND DateAdd ("m",[Dur_mths], [Start_date])
>= DateSerial(2012,1+2, 0)
,[Value]/[Dur_mths],0)) AS [M2],
Sum (iif([Start_date]
<= '2012-3-1'
AND DateAdd ("m",[Dur_mths], [Start_date])
>= DateSerial(2012,1+3, 0)
,[Value]/[Dur_mths],0)) AS [M3],
([M1]+[M2]+[M3]) AS [Q]
FROM [Data$]
LEFT JOIN [DataInternal$] ON [Data$].[Surname] = [DataInternal$].[Surname]
WHERE 1 = 1 AND [reg] = 'N'
GROUP BY [Data$].[Surname],[Code],[reg] ORDER BY [Data$].[Surname]
Empty lines added only for readability. The strings are first concatenated in excel to form a one SQL query string executed in vba macro. I will be grateful for all the comments and corrections. My question remains unadressed yet, if it is perhaps possible to loop such a solution in EXCEL ADO VBA.

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

SQL Stored Procedure: Business Hours

How can I create a stored procedure that accepts a start and end date.(e.g April 1 - April 30
1.) Get the business days including Saturdays x (a value). +
2.) Get Holidays x (a value)
and return the total.
I'm new to this, I guess it would be a tsql function. hmm.
any help would be appreciated.
Thanks
The simplest solution to this problem is to create a Calendar table that contains a value for every day you might want to consider. You could then add columns that indicate whether it is a business day or a holiday. With that, the problem becomes trivial:
Select ..
From Calendar
Where IsBusinessDay = 1
And Calendar.[Date] Between '2010-04-01' And '2010-04-30'
If you wanted the count of days, you could then do:
Select Sum( Case When IsBusinessDay = 1 Then 1 Else 0 End ) As BusinessDayCount
, Sum( Case When IsHoliday = 1 Then 1 Else 0 End ) As HolidayCount
From Calendar
Where Calendar.[Date] Between '2010-04-01' And '2010-04-30'
http://classicasp.aspfaq.com/date-time-routines-manipulation/how-do-i-count-the-number-of-business-days-between-two-dates.html
First, you will need to store all of the holidays into an independant table (Christmas, Easter, New Year Day, etc. with their respective dates (normally timed at midnight));
Second, you will have to generate, into a temporary table maybe, the dates of the office days, it then excludes the dates contained in the Holidays table.
Third, you may set the office hours to these dates depending on what day it is, if you have different working hours on different day.
That is the algorithm for you to find the appropriate code implementation.
Let me know if this helps!