Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Extreme DAX

You're reading from   Extreme DAX Take your Power BI and Microsoft data analytics skills to the next level

Arrow left icon
Product type Paperback
Published in Jan 2022
Publisher Packt
ISBN-13 9781801078511
Length 470 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Henk Vlootman Henk Vlootman
Author Profile Icon Henk Vlootman
Henk Vlootman
Michiel Rozema Michiel Rozema
Author Profile Icon Michiel Rozema
Michiel Rozema
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface Part I: Introduction FREE CHAPTER
1.1 DAX in Business Intelligence 1.2 Model Design 1.3 Using DAX 1.4 Context and Filtering Part II: Business cases
2.1 Security with DAX 2.2 Dynamically Changing Visualizations 2.3 Alternative Calendars 2.4 Working with AutoExist 2.5 Intercompany Business 2.6 Exploring the Future: Forecasting and Future Values 2.7 Inventory Analysis 2.8 Personnel Planning Other Books You May Enjoy
Index

Calculating sales

To start with a straightforward measure, let us calculate the total sales amount:

Total Sales = 
CALCULATE(
    SUM(fProjectSales[Budget]),
    USERELATIONSHIP(fProjectSales[StartDate], 'Calendar'[Date])
)

As USERELATIONSHIP activates the relationship between the fProjectSales table and the Calendar table on the StartDate column, this measure returns the amount sold in each month. While this is valuable information in terms of order intake, when working with projects that may span multiple years, another valuable insight would be to have the project budget spread out over the duration of the project.

There are several ways to do this. The easiest would be to divide the project budget by the duration of the project, and take that amount for each month that the project will run. This assumes that the income from the project will be evenly spread over the months.

While the duration of a project could be derived from the fFTE table, a more convenient approach would be to have a duration column in the ProjectType table; after all, the duration is a fixed attribute of a project type. As the fFTE table starts counting months from zero, let us just take the highest month number, which is then one less than the actual duration of the project type. To avoid confusion, we will name this column MaxMonth:

MaxMonth = CALCULATE(MAX(fFTE[Month]))

Note that we have to use CALCULATE here to make sure we get the largest Month value for the corresponding project types, not for all project types.

Let us assume that all calculations can be done on a month-by-month basis. The first month of the project, which is the month in which the start date of the project falls, is month zero; the last month is month zero plus MaxMonth.

For the calculations in this chapter, it is important to understand the consequences of this assumption. As an example, consider a project with February 15, 2022, as the start date, and a MaxMonth value of 3. This MaxMonth value means that we have four months of fFTE data, for February, March, April, and May, 2022. The project budget will be spread over these four months. You could be tempted to think that the duration of this project is four months, meaning that the project would run until June 15; however, the month of June does not count for our calculations.

This means that, given a selection on the Calendar, a project is active during (part of) that time period when:

  • The first month of the project starts before the last date in the Calendar selection;
  • And the project's last month ends after the first date in the selection.

The figure below illustrates this schematically for a few projects with MaxMonth = 2.

Figure 2.8.3: Active projects

Our first challenge is to determine the projects that are active during the selection on the Calendar table. The DAX formula below starts by filtering the fProjectSales table, implementing the logic outlined above:

Sales (over time) =
VAR MaxDate = MAX('Calendar'[Date])
VAR MinDate = MIN('Calendar'[Date])
RETURN
SUMX(
    FILTER(
        fProjectSales,
        EOMONTH(fProjectSales[StartDate], -1) + 1
        <= MaxDate
        && EOMONTH(
            fProjectSales[StartDate], RELATED(ProjectType[MaxMonth]
        ) >= MinDate
    ),

Let us pause here for a while and see what is happening. After storing the first and last days of the Calendar selection in variables, we traverse the fProjectSales table and apply the logic to retrieve the projects active within the selected period. The EOMONTH function adds a number of months to a date value, in this case fProjectSales[StartDate], then moves that date to the end of the month. The first EOMONTH expression returns the last day of the month preceding the start date, and adding 1 day gives us the first day of month zero. Adding MaxMonth in the second EOMONTH expression brings us to the last day of the last month of the project.

Now that we have selected the right projects, we can continue calculating the desired results:

    VAR ProjectBudget = fProjectSales[Budget]
    VAR ProjectStartDate = fProjectSales[StartDate]
    VAR ProjectMaxMonth = RELATED(ProjectType[MaxMonth])
    VAR MonthlyBudget = 
        DIVIDE(
            ProjectBudget, 
            ProjectMaxMonth + 1
        )
    VAR ActiveMonths =
    CALCULATETABLE(
        DISTINCT('Calendar'[YearMonthCtr]),
        KEEPFILTERS(
            'Calendar'[Date] >= ProjectStartDate
            && 'Calendar'[Date] <= 
            EOMONTH(ProjectStartDate, ProjectMaxMonth)
        )
    )
    RETURN
    COUNTROWS(ActiveMonths) * MonthlyBudget
)

After declaring the variables containing the values we need for the calculation, the ActiveMonths variable is the list of months (or YearMonthCtr values – this being a continuously increasing counter at the month level) in the Calendar selection that are within the duration of the project: between the start date and the end date. Note the use of KEEPFILTERS here to avoid losing any filters on the Calendar table; in this way, we get the overlap between the Calendar selection and the project duration.

The end result by project is the number of active months multiplied by the monthly project budget, which is the part of the total project budget expected to be spent during the selected period.

The figure below shows the difference between the Total Sales and Sales (over time) measures, for two sample projects:

A picture containing graphical user interface

Description automatically generated

Figure 2.8.4: Total Sales and Sales (over time)

In the chart, one project can be seen that starts in January 2021. The last month of this project is April 2022, and an evenly distributed amount is reported in every month between. A second project starts in February 2021, with a smaller total budget. The duration of the project is shorter, though; the budget is distributed over 5 months, indicating a MaxMonth value of 4.

Optimizing the sales calculation

The flaw in the Sales (over time) measure is, as you may have spotted already, that we iterate over a fact table. While you may assume that the number of multi-million projects sold is not super high, it is still worthwhile to see if we can change that.

The clear indicator here is the use of RELATED to retrieve information from a filter table in the process. Whenever your table aggregation contains the RELATED function, you should think about whether you can iterate over the filter table instead. In our case, the question is whether we can do the same calculation while iterating over the ProjectType table, and more specifically, the (unique) values of ProjectType[MaxMonth].

If we were to iterate over VALUES(ProjectType[MaxMonth]), we could calculate the total budget for all projects with that duration at once. Unfortunately, that does not help us completely: the calculation of the overlap between selected months and project duration months is not possible with only the duration.

For this alternative approach to work, we would also need to take the Calendar selection into account. Instead of doing the calculation for all months in the selection at once, we can go through the selection month by month. So, we iterate over the combinations of MaxMonth and YearMonthCtr values that are found in the query context. The table with combinations can be created using the CROSSJOIN function:

Sales (over time, optimized) =
VAR MonthDurationCombinations =
    CROSSJOIN(
        DISTINCT(ProjectType[MaxMonth]),
        DISTINCT('Calendar'[YearMonthCtr])
    )
RETURN
SUMX(
    MonthDurationCombinations,
    VAR ThisMaxMonth = ProjectType[MaxMonth]
    VAR ThisYearMonthCtr = 'Calendar'[YearMonthCtr]
    VAR MaxDate = 
    CALCULATE(
        MAX('Calendar'[Date]),
        ALL('Calendar'),
        'Calendar'[YearMonthCtr] = ThisYearMonthCtr
    )
    VAR MinDate =
    CALCULATE(
        MIN('Calendar'[Date]),
        ALL('Calendar'),
        'Calendar'[YearMonthCtr] = ThisYearMonthCtr – ThisMaxMonth
    )
    VAR TotalBudget = 
    CALCULATE(
        SUM(fProjectSales[Budget]),
        fProjectSales[StartDate] <= MaxDate
        && fProjectSales[StartDate] >= MinDate
    )
    RETURN
    DIVIDE(TotalBudget, ThisMaxMonth + 1)
)

For each combination in the MonthDurationCombinations table, we first store the values in the ThisMaxMonth and ThisYearMonthCtr variables. Next, we need to select the projects active in this particular month. We can do that using their start date, for which we have to derive the earliest, MinDate, and latest, MaxDate, possible start dates for a project to be active. The latest possible start date is, of course, the last day in this month; any project starting later is not yet active in this month. As for the earliest start dates, we can subtract ThisMaxMonth from the YearMonthCtr to find the earliest month possible. Any project starting earlier will have finished before this month starts.

To illustrate this, the example in the figure below shows, for month 66 and MaxMonth 2, that the earliest possible start date of an active project is the first day of month 64. Any day earlier, like the upper project, and the month zero of the project is month 63 and there is no budget left for month 66.

Figure 2.8.5: Finding the earliest possible start date

With this, we can compute the total budget of all active projects at once and, as the calculation is done by MaxMonth value, it is easy to divide this by the current duration and retrieve the budget projected for the current month.

The calculations involving MaxMonth are the reason why we use the month counter here, and not simply a month number: with the counter, we don't have to worry about year boundaries.

The number of iterations that SUMX has to do is equal to the number of different durations multiplied by the number of months in the query context. This will typically be only a small number and may be far better than traversing through all projects.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image