4
votes

I’m trying to work out the DAX expression [for MS PowerPivot in Excel 2010] to evenly distribute the sum of a value across the range it’s applied to, and re-sum that up for a given time span/period. It’s trivial to cross-apply in SQL server, though with every attempt, end up with the same wrong result.

I’ve got the output from MS Project saved as Excel and imported/transformed using PowerQuery, so the start and finish/end dates are proper dates, the {X}h and {Y}d are integers, and the calendar day duration between them are already calculated/formatted for the model. I also have a dates table created that has the contiguous dates from the first date through the last, and a years table that has the string representation of the 4 digit years I want to summarize by.

The model looks like so:

PowerPivot Model

I have created calculated columns on the ResourceQuery, TaskQuery and AssignmentQuery tables (all directly taken from the MS Project output), and on the ServiceAreaQuery (unique values from TaskQuery … essentially subprojects). Each also has a simple measure that is the sum of the Assigned hours column.

The data itself looks like you’d expect from a Project 2010 file, and has a {start_date}, {finish_date} and hours. The dates for a task can span anywhere from 1 day to 5 years … and this is where my problem lies.

How do I split/chunk the pre-summed value for a long running tasks to match the time interval I’m looking for?

Even if I use the year column from the date table, the time intelligence doesn’t catch it & I’m running out of ideas for a CALCULATE(SUM(FILTER(COUNTROWS(DATESBETWEEN)))) type of thing.

There are two intermediate steps I've tried to figure out to no avail. I’d imagine they are both solved by the same effective function to get to the end goal of hours, by service area, by resource, by year.

Pivot table to show

  • Hours by resource, by year
  • Hours by service area, by year

in order to show the end goal of

  • Hours, by service area, by resource, by year

You can see the issue in the output below.

Bad Output Example

Note that when using the total of assigned hours, and the resource name from AssignmentQuery, I get the right sums, but when using any date value … I only get the hours against the start date (the active relationship in the model). What I need is for those hours to be evenly spread across the period that they’re applicable to (so if something has 1,000 hours between 1/1/16 and 1/1/19 I’d expect 333 hours/year to show).

My initial thought is that the selector/filter/calculate function needs to do the following:

  • Select the hours for the person
  • Select the days in the period filtered to (e.g. month/year/quarter/whatever) from either a filter or as a column header
  • Calculate the hours per day
  • Get the working days in the filtered period
  • select the sum of the hours from the overlap

Any ideas are greatly appreciated! I’m open to doing some additional ETL/data creation as a PowerQuery step, but would really like to figure out the right DAX expression for this so it can be something available as a time-slicer/filter on the project.

Thanks in advance.

** Edit to post the revised version of the answer provided **

[Hours Apportioned Raw] :=
DIVIDE (
    CALCULATE (
                [Hours],
                FILTER (
                    AssignmentQuery,
                    AssignmentQuery[Start_Date] <= MAX ( Dates[Date] )
                        && AssignmentQuery[Finish_Date] >= MAX ( Dates[Date] )
                        )
                )
    , ( COUNTROWS (
                    DATESBETWEEN ( 
                                    Dates[Date]
                                    , FIRSTDATE ( AssignmentQuery[Start_Date] )
                                    , LASTDATE ( AssignmentQuery[Finish_Date] )
                                 )
                  )
      )
)
1

1 Answers

6
votes

Given that you have a relatively complex model in place and your requirement is not totally straightforward, I'm not sure that this will get you all the way there but hopefully it will at least either give you the inspiration to modify it for your purposes or start a more detailed discussion.

The measures below effectively sum the hours, apply them to dates where the dates are between the start and end and divides the total by the number of days. The slight complexity is this needs to be iterated x2 - once over dates and once over rows in the table containing the hours.

An issue for you might be that I'm using an unconnected date table and if you can't replicate this situation in your model then we will need to try using some ALL() functions instead.

Solution below assumes a table called 'data' that has 4 columns: id, start, end, value and table called calendar that has 2 columns Date and Month.

Measure 1: Sum the hours

[Hours] =SUM(Data[Value])

Measure 2: Apply the hours to the dates and divide by number of dates

[Hours Apportioned Raw] =
 CALCULATE ([Hours],
FILTER (
    Data,
    Data[Start] <= MAX ( Calendar[Date] )
        && Data[END] >= MAX ( Calendar[Date] )
       )
            )
/ ( MAX ( Data[End] ) - MAX ( Data[Start] ) )

Measure 3: Iterate Measure 2 over dates and ids to give correct values

=
    SUMX (
        VALUES ( Calendar[Date] ),
        SUMX ( VALUES ( Data[ID] ), [Hours Apportioned RAW] )
    )

Hope this makes some sense, very simple test model here: Test Model

Note you will need to download the model not just view it in the browser.