Is it possible to create a report that sums hours for a day grouped by an Id using a start and end time stamp?
I need to be able to split time that spans days and take part of that time and sum to the correct date group.
NOTE: The date ids are to a date dimension table.
------------------------------------------------------------------------------
TaskId | StartDateId | EndDateId | StartTime | EndTime
------------------------------------------------------------------------------
2 | 20190317 | 20190318 | 2019-03-17 16:30:00 | 2019-03-18 09:00:00
------------------------------------------------------------------------------
1 | 20190318 | 20190318 | 2019-03-18 09:00:00 | 2019-03-18 16:30:00
------------------------------------------------------------------------------
2 | 20190318 | 20190319 | 2019-03-18 16:30:00 | 2019-03-19 09:00:00
------------------------------------------------------------------------------
So based on this, the desired report output would be:
-------------------------
Date | Task | Hours
-------------------------
2019-03-17 | 2 | 7.5
-------------------------
2019-03-18 | 1 | 7.5
-------------------------
2019-03-18 | 2 | 16.5
-------------------------
...
The only working solution I have managed to implement is splitting records so that no record spans multiple days. I was hoping to find a report query solution, rather than an ETL base based solution.
I have tried to simulate your problem here: https://rextester.com/DEV45608 and I hope it helps you :) (The CTE GetDates can be replaced by your date dimension)
DECLARE #minDate DATE
DECLARE #maxDate DATE
CREATE TABLE Tasktime
(
Task_id INT,
Start_time DATETIME,
End_time DATETIME
);
INSERT INTO Tasktime VALUES
(2,'2019-03-17 16:30:00','2019-03-18 09:00:00'),
(1,'2019-03-18 09:00:00','2019-03-18 16:30:00'),
(2,'2019-03-18 16:30:00','2019-03-19 09:00:00');
SELECT #mindate = MIN(Start_time) FROM Tasktime;
SELECT #maxdate = MAX(End_time) FROM Tasktime;
;WITH GetDates AS
(
SELECT 1 AS counter, #minDate as Date
UNION ALL
SELECT counter + 1, DATEADD(day,counter,#minDate)
from GetDates
WHERE DATEADD(day, counter, #minDate) <= #maxDate
)
SELECT counter, Date INTO #tmp FROM GetDates;
SELECT
g.Date,
t.Task_id,
SUM(
CASE WHEN CAST(t.Start_time AS DATE) = CAST(t.End_time AS DATE) THEN
DATEDIFF(second, t.Start_time, t.End_time) / 3600.0
WHEN CAST(t.Start_time AS DATE) = g.Date THEN
DATEDIFF(second, t.Start_time, CAST(DATEADD(day,1,g.Date) AS DATETIME)) / 3600.0
WHEN CAST(t.End_time AS DATE) = g.Date THEN
DATEDIFF(second, CAST(g.Date AS DATETIME), t.End_time) / 3600.0
ELSE
24.0
END) AS hours_on_the_day_for_the_task
from
#tmp g
INNER JOIN
Tasktime t
ON
g.Date BETWEEN CAST(t.Start_time AS DATE) AND CAST(t.End_time AS DATE)
GROUP BY g.Date, t.Task_id
The Desired Date can be joined to the date dimension and return the "calendar date" and you can show that date in the report.
As for the HOURS.. when you are retrieving your dataset in SQL, just do this.. it is as simple as:
cast(datediff(MINUTE,'2019-03-18 16:30:00','2019-03-19 09:00:00') /60.0 as decimal(13,1)) as 'Hours'
So in your case it would be
cast(datediff(MINUTE,sometable.startdate,sometable.enddate) /60.0 as decimal(13,1)) as 'Hours'
Just doing a HOUR will return the whole hour.. and dividing by 60 will return a whole number. Hence the /60.0 and the cast
Related
I've got this data set where I'm creating a report per shift displaying a certain activity per hour. Eg.: A shift runs from 7-15, so the report will be:
7:00 - 15 moves
8:00 - 18 moves
This report contains filters for weeks and days, as the goal is for users to see their shift performance.
However, I'd like to compare the activities in a specific hour with the same hour, same day in the last 10 weeks. So for example:
I've got 15 moves in hour 7:00 of a Tuesday and i'd like to compare that with the average move count on 7:00 of a Tuesday in the last 10 weeks.
How would I get this in SQL! :-)
Here is something that might get you moving in the right direction. Given that you have not stated your data structure, I have taken the liberty to assume what that may be.
You can run the following in SSMS to review/modify the results yourself.
First, I created a table variable to simulate the assumed data.
-- Create a dummy "shift" table --
DECLARE #shifts TABLE ( shift_id INT IDENTITY ( 1, 1 ) PRIMARY KEY, shift_date DATETIME, shift_moves INT );
Then I inserted some dummy data. For the sake of time and simplicity, I stuck with Tuesdays over the last ten weeks.
-- Insert ten weeks of Tuesdays --
INSERT INTO #shifts ( shift_date, shift_moves ) VALUES
( '11/06/2018 07:10:00', 5 )
, ( '11/13/2018 07:08:00', 12 )
, ( '11/20/2018 07:00:00', 14 )
, ( '11/27/2018 07:20:00', 15 )
, ( '12/04/2018 07:35:00', 12 )
, ( '12/11/2018 07:18:00', 11 )
, ( '12/18/2018 07:16:00', 10 )
, ( '12/25/2018 07:00:00', 12 )
, ( '01/01/2019 07:00:00', 13 )
, ( '01/08/2019 07:22:00', 15 );
The table variable #shifts now contains the following data:
+----------+-------------------------+-------------+
| shift_id | shift_date | shift_moves |
+----------+-------------------------+-------------+
| 1 | 2018-11-06 07:10:00.000 | 5 |
| 2 | 2018-11-13 07:08:00.000 | 12 |
| 3 | 2018-11-20 07:00:00.000 | 14 |
| 4 | 2018-11-27 07:20:00.000 | 15 |
| 5 | 2018-12-04 07:35:00.000 | 12 |
| 6 | 2018-12-11 07:18:00.000 | 11 |
| 7 | 2018-12-18 07:16:00.000 | 10 |
| 8 | 2018-12-25 07:00:00.000 | 12 |
| 9 | 2019-01-01 07:00:00.000 | 13 |
| 10 | 2019-01-08 07:22:00.000 | 15 |
+----------+-------------------------+-------------+
I created a few parameters that you might pass to a stored procedure.
-- What date are we looking at? --
DECLARE #date DATETIME = '01/08/2019';
-- How many weeks back to compare? --
DECLARE #weeks_back INT = -10;
*Remember: To look backward, #weeks_back MUST be a negative number. In production, you would have a check/handle for this.
Next, I created two local variables to help keep the date/time simple for use when querying.
-- Create variables for the start and end times for simplicity --
DECLARE
#sDT DATETIME = CAST( CONVERT( VARCHAR(10), #date, 101 ) + ' 00:00:00' AS DATETIME ),
#eDT DATETIME = CAST( CONVERT( VARCHAR(10), #date, 101 ) + ' 23:59:59' AS DATETIME );
Then, it was a matter of querying the data for the desired results using CROSS APPLY.
Using CROSS APPLY allows me to query a second subset of shift data that is tied to the primary record queried ( in this case a shift_date of 01/08/2019 ) for the desired time frame ( #weeks_back ).
-- Get resultset --
SELECT
DATEPART( hh, shift_date ) AS [shift_hour]
, DATENAME( dw, s.shift_date ) AS [shift_day]
, CONVERT( VARCHAR(10), s.shift_date, 101 ) AS [shift_date]
, s.shift_moves
, shift_avg.shift_average
FROM #shifts AS s
CROSS APPLY (
SELECT
AVG( a.shift_moves ) AS [shift_average]
FROM #shifts a
WHERE
-- restrict to the current hour.
DATEPART( HH, a.shift_date ) = DATEPART( HH, s.shift_date )
-- restrict to the current day of the week.
AND DATEPART( DW, a.shift_date ) = DATEPART( DW, s.shift_date )
-- compare against the desired time period / weeks back.
AND a.shift_date BETWEEN DATEADD( WW, #weeks_back, CAST( CONVERT( VARCHAR(10), s.shift_date, 101 ) AS DATETIME ) ) AND a.shift_date
) AS shift_avg
WHERE
s.shift_date BETWEEN #sDT AND #eDT
ORDER BY
s.shift_date;
Which returns the following resultset:
+------------+-----------+------------+-------------+---------------+
| shift_hour | shift_day | shift_date | shift_moves | shift_average |
+------------+-----------+------------+-------------+---------------+
| 7 | Tuesday | 01/08/2019 | 15 | 11 |
+------------+-----------+------------+-------------+---------------+
I hope this helps get you moving in the desired direction, #Aron.
I have a table with a column visit_date that is a datetime object with format YYYY-MM-DD HH:MI:SS that looks like the following:
visit_date |visit_id
-------------------|-----
2010-11-01 00:02:00|92314
2010-11-01 23:05:21|23498
2010-11-01 12:42:31|12343
2010-11-02 05:13:21|79881
2010-11-02 14:35:15|22134
2010-11-02 16:12:23|12348
2010-11-03 01:22:44|12384
2010-11-03 05:23:41|12394
2010-11-03 15:13:55|99384
I would like to group by date and by 8-hr window on that date such that I have:
interval |count
-------------------|-----
2010-11-01 00:00:00|1
2010-11-01 08:00:00|2
2010-11-01 16:00:00|3
2010-11-02 00:00:00|4
2010-11-02 08:00:00|5
2010-11-02 16:00:00|6
2010-11-03 00:00:00|7
2010-11-03 08:00:00|8
2010-11-03 16:00:00|9
My original query (using only dates) was:
SELECT CAST(visit_date as DATE), count(1) as count
FROM table
GROUP BY CAST(visit_date as DATE)
ORDER BY CAST(visit_date as DATE)
But that only groups by date.
Is there a recommended way to get interval counts for each interval per day? I have seen implementations using DATEADD and DATEPART but not sure which makes the most sense in this situation.
Thanks!
Add the hours in to what you group and count:
SELECT
CAST(visit_date as DATE),
HOUR(visit_date)/8 as ival8h
count(1) as count
FROM table
GROUP BY CAST(visit_date as DATE), HOUR(visit_date)/8
ORDER BY CAST(visit_date as DATE)
The hour function returns the hour number of the passed date, divide it by 8 to get an int of the interval, so 0 to 7 becomes 0, 8 to 16 becomes 1 etc
If you want it back as a time pegged to a round 8h multiply it by 8 again and format it to NN:00:00, or add it to the date, thus:
SELECT
DATEADD(hour, (HOUR(visit_date)/8)*8, CAST(CAST(visit_date as DATE) as DATETIME) as quantized_date,
count(1) as count
FROM table
GROUP BY DATEADD(hour, (HOUR(visit_date)/8)*8, CAST(CAST(visit_date as DATE) as DATETIME)
ORDER BY CAST(visit_date as DATE)
This basically rounds the hours down to the lesser 8h market and adds that to midnight. Two casts are required (probably) on the date because DATEADD won't add hours to a date, only a datetime but we need the cast to date to peg the tine element to midnight
If you want there to be a date and a 0 count for periods where no events took place, use a numbers table or row generator and create a sequence of dates to left join your real data onto, then count the real data grouped by the fake dates
Use a cross apply to form 4 shift boundary values, then use those in a case expression to genertate the group by values
SELECT
case
when visit_date >= s1 and visit_date < s2 then s1
when visit_date >= s2 and visit_date < s3 then s2
when visit_date >= s3 and visit_date < s4 then s3
end as shift
, count(1) as count
FROM mytable
CROSS APPLY (
select
cast(CAST(visit_date as DATE)as datetime) s1
, dateadd(hh,8,cast(CAST(visit_date as DATE)as datetime)) s2
, dateadd(hh,16,cast(CAST(visit_date as DATE)as datetime)) s3
, dateadd(hh,24,cast(CAST(visit_date as DATE)as datetime)) s4
) ca
GROUP BY
case
when visit_date >= s1 and visit_date < s2 then s1
when visit_date >= s2 and visit_date < s3 then s2
when visit_date >= s3 and visit_date < s4 then s3
end
ORDER BY shift
result:
+----+---------------------+-------+
| | shift | count |
+----+---------------------+-------+
| 1 | 01.11.2010 00:00:00 | 1 |
| 2 | 01.11.2010 08:00:00 | 1 |
| 3 | 01.11.2010 16:00:00 | 1 |
| 4 | 02.11.2010 00:00:00 | 1 |
| 5 | 02.11.2010 08:00:00 | 1 |
| 6 | 02.11.2010 16:00:00 | 1 |
| 7 | 03.11.2010 00:00:00 | 2 |
| 8 | 03.11.2010 08:00:00 | 1 |
+----+---------------------+-------+
I think the canonical way in SQL Server is to use dateadd() and datediff():
select dateadd(hour, 0, 3 * (datediff(hour, 0, visit_date) / 3)) as day_hour8,
count(*)
from t
group by dateadd(hour, 0, 3 * (datediff(hour, 0, visit_date) / 3))
order by day_hour8;
I have a SQLite database with start and stop datetimes
With the following SQL query I get the difference hours between start and stop:
SELECT starttime, stoptime, cast((strftime('%s',stoptime)-strftime('%s',starttime)) AS real)/60/60 AS diffHours FROM tracktime;
I need a SQL query, which delivers the sum of multiple timestamps, grouped by every day (also whole dates between timestamps).
The result should be something like this:
2018-08-01: 12 hours
2018-08-02: 24 hours
2018-08-03: 12 hours
2018-08-04: 0 hours
2018-08-05: 1 hours
2018-08-06: 14 hours
2018-08-07: 8 hours
You can try this, use CTE RECURSIVE make a calendar table for every date start time and end time, and do some calculation.
Schema (SQLite v3.18)
CREATE TABLE tracktime(
id int,
starttime timestamp,
stoptime timestamp
);
insert into tracktime values
(11,'2018-08-01 12:00:00','2018-08-03 12:00:00');
insert into tracktime values
(12,'2018-09-05 18:00:00','2018-09-05 19:00:00');
Query #1
WITH RECURSIVE cte AS (
select id,starttime,date(starttime,'+1 day') totime,stoptime
from tracktime
UNION ALL
SELECT id,
date(starttime,'+1 day'),
date(totime,'+1 day'),
stoptime
FROM cte
WHERE date(starttime,'+1 day') < stoptime
)
SELECT strftime('%Y-%m-%d', starttime),(strftime('%s',CASE
WHEN totime > stoptime THEN stoptime
ELSE totime
END) -strftime('%s',starttime))/3600 diffHour
FROM cte;
| strftime('%Y-%m-%d', starttime) | diffHour |
| ------------------------------- | -------- |
| 2018-08-01 | 12 |
| 2018-09-05 | 1 |
| 2018-08-02 | 24 |
| 2018-08-03 | 12 |
View on DB Fiddle
In my database I have a Reservation table and it has three columns Initial Day, Last Day and the House Id.
I want to count the total days and omit those who are repeated, for example:
+-------------+------------+------------+
| | Results | |
+-------------+------------+------------+
| House Id | InitialDay | LastDay |
+-------------+------------+------------+
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-22 |
| 20 | 2017-09-18 | 2017-09-22 |
+-------------+------------+------------+
If you noticed the House Id with the number 1 has two rows, and each row has dates but the first row is in the interval of dates of the second row. In total the number of days should be 5 because the first shouldn't be counted as those days already exist in the second.
The reason why this is happening is that each house has two rooms, and different persons can stay in that house on the same dates.
My question is: how can I omit those cases, and only count the real days the house was occupied?
In your are using SQL Server 2012 or higher you can use LAG() to get the previous final date and adjust the initial date:
with ReservationAdjusted as (
select *,
lag(LastDay) over(partition by HouseID order by InitialDay, LastDay) as PreviousLast
from Reservation
)
select HouseId,
sum(case when PreviousLast>LastDay then 0 -- fully contained in the previous reservation
when PreviousLast>=InitialDay then datediff(day,PreviousLast,LastDay) -- overlap
else datediff(day,InitialDay,LastDay)+1 -- no overlap
end) as Days
from ReservationAdjusted
group by HouseId
The cases are:
The reservation is fully included in the previous reservation: we only need to compare end dates because the previous row is obtained ordering by InitialDay, LastDay, so the previous start date is always minor or equal than the current start date.
The current reservation overlaps with the previous: in this case we adjust the start and don't add 1 (the initial day is already counted), this case include when the previous end is equal to the current start (is a one day overlap).
There is no overlap: we just calculate the difference and add 1 to count also the initial day.
Note that we don't need extra condition for the reservation of a HouseID because by default the LAG() function returns NULL when there isn't a previous row, and comparisons with null always are false.
Sample input and output:
| HouseId | InitialDay | LastDay |
|---------|------------|------------|
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 1 | 2017-09-21 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-27 |
| 19 | 2017-09-24 | 2017-09-26 |
| 19 | 2017-09-29 | 2017-09-30 |
| 20 | 2017-09-19 | 2017-09-22 |
| 20 | 2017-09-22 | 2017-09-26 |
| 20 | 2017-09-24 | 2017-09-27 |
| HouseId | Days |
|---------|------|
| 1 | 5 |
| 19 | 12 |
| 20 | 9 |
select house_id,min(initialDay),max(LastDay)
group by houseId
If I understood correctly!
Try out and let me know how it works out for you.
Ted.
While thinking through your question I came across the wonder that is the idea of a Calendar table. You'd use this code to create one, with whatever range of dates your want for your calendar. Code is from http://blog.jontav.com/post/9380766884/calendar-tables-are-incredibly-useful-in-sql
declare #start_dt as date = '1/1/2010';
declare #end_dt as date = '1/1/2020';
declare #dates as table (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime
)
while #start_dt < #end_dt
begin
insert into #dates(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts
)
values(
#start_dt, year(#start_dt), month(#start_dt), day(#start_dt),
datepart(weekday, #start_dt), datename(weekday, #start_dt), datename(month, #start_dt), datepart(dayofyear, #start_dt), datepart(quarter, #start_dt),
dateadd(day,-(day(#start_dt)-1),#start_dt), dateadd(day,-(day(dateadd(month,1,#start_dt))),dateadd(month,1,#start_dt)),
cast(#start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, #start_dt) as datetime))
)
set #start_dt = dateadd(day, 1, #start_dt)
end
select *
into Calendar
from #dates
Once you have a calendar table your query is as simple as:
select distinct t.House_id, c.date_id
from Reservation as r
inner join Calendar as c
on
c.date_id >= r.InitialDay
and c.date_id <= r.LastDay
Which gives you a row for each unique day each room was occupied. If you need a sum of how many days each room was occupied it becomes:
select a.House_id, count(a.House_id) as Days_occupied
from
(select distinct t.House_id, c.date_id
from so_test as t
inner join Calendar as c
on
c.date_id >= t.InitialDay
and c.date_id <= t.LastDay) as a
group by a.House_id
Create a table of all the possible dates and then join it to the Reservations table so that you have a list of all days between InitialDay and LastDay. Like this:
DECLARE #i date
DECLARE #last date
CREATE TABLE #temp (Date date)
SELECT #i = MIN(Date) FROM Reservations
SELECT #last = MAX(Date) FROM Reservations
WHILE #i <= #last
BEGIN
INSERT INTO #temp VALUES(#i)
SET #i = DATEADD(day, 1, #i)
END
SELECT HouseID, COUNT(*) FROM
(
SELECT DISTINCT HouseID, Date FROM Reservation
LEFT JOIN #temp
ON Reservation.InitialDay <= #temp.Date
AND Reservation.LastDay >= #temp.Date
) AS a
GROUP BY HouseID
DROP TABLE #temp
I'm currently working on some reports from MS Project Server and found this oddity:
For some obscure reason, whenever you appoint to the same task with the same amount of time in consecutive days, instead of creating an entry for each appointment, the application updates the start date and the finish date fields on database, leaving only one entry for that task, but with a range between the dates.
If the amount of time appointed to the task in consecutive days are different, then there will be created one entry per appointment.
(Yes, I know, it's kind of confusing. I don't even know how to explain this better).
I want to know if it is somehow possible to generate more rows within SQL statement whenever there is a difference between the start and the finish date, one for each day in the range.
This is the query I have right now, I already can tell which rows have this date difference, but I don't know what I can do next.
select
r.WRES_ID, r.RES_NAME, PROJ_NAME, p.WPROJ_ID, TASK_NAME, WWORK_VALUE, WWORK_START, WWORK_FINISH,
datediff(d, WWORK_START, WWORK_FINISH) + 1 AS work_days
from MSP_WEB_RESOURCES r
join
MSP_WEB_ASSIGNMENTS a on a.WRES_ID = r.WRES_ID
join
MSP_WEB_PROJECTS p on p.WPROJ_ID = a.WPROJ_ID
join
MSP_WEB_WORK w on w.WASSN_ID = a.WASSN_ID
where RES_NAME = 'HenriqueBarcelos'
and WWORK_TYPE = 1
and WWORK_VALUE > 0
and WWORK_FINISH between '2014-01-27' and '2014-01-31'
order by WWORK_FINISH DESC
I know I could do this at the application level, but I was wondering if I could just do it within the database itself.
Thank's in advance.
Edit:
These are my current results:
WRES_ID | RES_NAME | TASK_NAME | WWORK_VALUE | WWORK_START | WWORK_FINISH | work_days
--------+------------------+-------------------------+---------------+---------------------+---------------------+----------
382 | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000 | 2014-01-30 00:00:00 | 2014-01-30 00:00:00 | 1
382 | HenriqueBarcelos | Internal Training | 289800.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382 | HenriqueBarcelos | Outsourcing Initiatives | 120000.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382 | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000 | 2014-01-27 00:00:00 | 2014-01-28 00:00:00 | 2
382 | HenriqueBarcelos | Infrastructure (TI) | 120000.000000 | 2014-01-27 00:00:00 | 2014-01-27 00:00:00 | 1
Notice that the second last register has a range of 2 days. In deed, there are 2 appointments, one on Jan 27th and other on 28th.
What I want to do is expand this and return one entry per day in this case.
It can be done, but it's not very elegant. First you need a function that will expand the date range into sequence of dates:
CREATE FUNCTION ufn_Expand(#start DATE, #end DATE)
RETURNS TABLE
AS
RETURN
WITH cte AS
(
SELECT #start AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt) FROM cte WHERE dt < #end
)
SELECT dt FROM cte
Then use that in your query with CROSS APPLY:
SELECT /* your columns */, x.dt
FROM /* your joins */
CROSS APPLY ufn_Expand(WWORK_START, WWORK_FINISH) x
I'd use a numbers table (nice and set-based, yum!)
SELECT start_date
, end_date
, DateDiff(dd, start_date, end_date) + 1 As number_of_days --rows to display
FROM your_table
INNER
JOIN dbo.numbers
ON numbers.number BETWEEN 1 AND DateDiff(dd, start_date, end_date) + 1
Use your favourite search engine to find a numbers table script. Here's one I made earlier.
As an aside: if you remove the +1s you just modify the join to be between zero and the DateDiff() - I added the +1s as I thought it might be clearer!
You can see this from another perspective. You don't really want a row per each worked day. What you really need it's the number of worked days, multiplied by the reported worked time. Something like this:
(dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
however, this creates an issue. Let's say you want a given period. If you use the WWORK_START and WWORK_FINISH dates for your report, you need to be careful to include all the work with only some days inside the period. Something like this will do it:
DECLARE #InitDate DATETIME;
DECLARE #EndDate DATETIME;
SET #InitDate = '2016/06/01';
SET #EndDate = '2016/07/01';
--Full list of tasks
SELECT dbo.MSP_WEB_RESOURCES.RES_NAME AS Name, dbo.MSP_WEB_PROJECTS.PROJ_NAME AS Project,
dbo.MSP_WEB_WORK.WWORK_VALUE / 60000 AS ReportedWork,
CASE
WHEN WWORK_START < #InitDate THEN DATEDIFF(day, #InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --If the task started before the start of the period
WHEN WWORK_FINISH > DATEDIFF(day,-1,#EndDate) THEN DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,#EndDate)) + 1 --if the task ended after the end of the period
ELSE DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --All tasks with start and end date inside the period
END AS RepeatedDays,
CASE
WHEN WWORK_START < #InitDate THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, #InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
WHEN WWORK_FINISH > DATEDIFF(day,-1,#EndDate) THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,#EndDate)) + 1)
ELSE (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
END AS ActualWork,
dbo.MSP_WEB_WORK.WWORK_START,
dbo.MSP_WEB_WORK.WWORK_FINISH
FROM dbo.MSP_WEB_RESOURCES INNER JOIN
dbo.MSP_WEB_ASSIGNMENTS INNER JOIN
dbo.MSP_WEB_PROJECTS ON dbo.MSP_WEB_ASSIGNMENTS.WPROJ_ID = dbo.MSP_WEB_PROJECTS.WPROJ_ID INNER JOIN
dbo.MSP_WEB_WORK ON dbo.MSP_WEB_ASSIGNMENTS.WASSN_ID = dbo.MSP_WEB_WORK.WASSN_ID ON
dbo.MSP_WEB_RESOURCES.WRES_ID = dbo.MSP_WEB_ASSIGNMENTS.WRES_ID
WHERE (dbo.MSP_WEB_WORK.WWORK_TYPE = 1) AND
(
#InitDate BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
DATEADD(day,-1,#EndDate) BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
(dbo.MSP_WEB_WORK.WWORK_START >= #InitDate) AND
(dbo.MSP_WEB_WORK.WWORK_FINISH < #EndDate)
)
ORDER BY dbo.MSP_WEB_WORK.WWORK_START;