Reputation: 6206
This is a contrived example of some scheduling data we process.
The idea is to naively ask the question
Given that a project has N remaining unscheduled hours, a head_count (the number of people working on it), and the assumption that each worker will work 8 days; what non-weekend days AFTER the last scheduled day will this project require?
I have a simplified dim_dates table:
CREATE TABLE dim_dates (
date_actual DATE NOT NULL,
is_weekend BOOLEAN NOT NULL
);
And the projects table:
CREATE TABLE projects (
id SERIAL PRIMARY KEY NOT NULL,
last_scheduled_date DATE NOT NULL,
remaining_hours_required INT NOT NULL,
head_count INTEGER NOT NULL
);
So given this project:
INSERT INTO projects (last_scheduled_date, remaining_hours_required, head_count)
VALUES ('2018-01-04', 21, 1);
By taking CEIL(remaining_hours_required::decimal / (8 * head_count))
we end up with 3 days of work required. The date 2018-01-04 is a Thursday, so the dates we should end up with is the following Friday, Monday, and Tuesday.
To get the following non-weekend days we can easily join onto dim_dates:
SELECT p.*, d.*
FROM projects p
INNER JOIN dim_dates d ON (
d.date_actual > p.last_scheduled_date
AND
d.is_weekend IS FALSE
);
Which would give us all following dates that aren't weekends. My conundrum is how to filter this set down to 3 rows.
So the (imperative) task description is:
Here's a fiddle with data: http://sqlfiddle.com/#!17/f6a90/3
Upvotes: 1
Views: 95
Reputation: 29647
Using a running sum to keep the remaining hours, per project. And keeping those within limit.
In the sub-query it limits on a calculated maximum date.
So that it doesn't calculate for each next date from the dim_dates.
SELECT
id as projectId
, date_actual as weekDay
, row_number()
over (partition by id order by date_actual desc)-1 as daysRemaining
FROM
(
SELECT
p.id, d.date_actual, p.head_count
, p.last_scheduled_date, p.remaining_hours_required
,(p.remaining_hours_required
- sum(8*head_count) over
(partition by p.id order by date_actual)
) as remaining_hours
FROM projects p
JOIN dim_dates d
ON (d.date_actual > p.last_scheduled_date
AND d.date_actual <= p.last_scheduled_date + cast(ceil(((p.remaining_hours_required/5.0)*7.0)/(8*p.head_count))+1 as int)
AND d.is_weekend IS FALSE)
) q
WHERE remaining_hours > (-8*head_count)
ORDER BY id, date_actual;
Test on SQL fiddle here
Upvotes: 1
Reputation: 48187
WITH dates AS (
SELECT *,
row_number() OVER(ORDER BY date_actual ASC) AS rownum
FROM dim_dates
WHERE date_actual > '2018-01-04'::date
AND is_weekend IS FALSE
), remain_days AS (
SELECT CEIL (remaining_hours_required::decimal / (8 * head_count))
FROM projects p
)
SELECT *
FROM dates
JOIN remain_days
ON rownum <= ceil
Upvotes: 1