Quantcast
Channel: MarkGStacey » DAX
Viewing all articles
Browse latest Browse all 2

Dynamic Reporting Periods in DAX

$
0
0

Dynamic Reporting Periods

The workbook I created is here

NB: You may need to replace ; with , in all DAX depending on your localisation settings.

When working with tabular models (or PowerPivot workbooks) you often want to use relative date periods, as you’ll as a date to which the relative date period is associated. You may well want to start with Jason Thomas’ blog post on time periods here before reading further.

In order to achieve this, in addition to the standard date table, you are going to add a second date table, as well as a reporting period table to your model – and associate neither of them with your fact table.

In order to setup your base model, start by creating a new PowerPivot workbook, and adding FactResellerSales, and the directly associated tables, giving you a base model as in Figure 1 below:

Note that the date table is linked 3 times over, and only the OrderDate relationship is active. Based on this, we are going to use OrderDate as our primary table for this exercise and ignore the others (see here for a much better way to treat role playing dimensions than this default)

Next up, you create a second version of the date table – click on Existing Connections, and Open, then browse to the date table. Rename it to “DatePeriodSelector” as in Figure 2.

Add the table.

Now we need some custom periods. You need a new table – this table will contain all the reporting periods.

CREATE
TABLE
[dbo].[DatePeriods](

    [ID]
[int]
IDENTITY(1,1)
NOT
NULL,

    [Period]
[varchar](50)
NOT
NULL,

    [Number]
[int]
NOT
NULL,

    [PeriodName]
[varchar](50)
NOT
NULL

)

ID is just an internal identity – Period is the date range (to be used in a DATEADD – well, kinda), so DAY, MONTH or YEAR, number is the number of periods, and PeriodName is the display name we’ll use on the slicer. We need some data, so you can run these inserts for a couple of tests:

INSERT
INTO
[dbo].[DatePeriods]


([Period]


,[Number]

,[Name])

Select
‘Month’, 1, ‘Last Month’

UNION
ALL

Select
‘Year’, 1, ‘Last Year’

UNION
ALL

Select
‘Day’, 30, ’30 days’

UNION
ALL

Select
‘Day’, 7, ’1 week’

Now add this table to your model, as shown in Figure 3

So that’s the model setup, now onto the DAX.

To start, I am breaking the DAX up into a couple of sub-calcs. There are 2 reasons for this: the first is readability and ease of explanation, and the second is what appears to be a bug when combining the SWITCH statement and a LASTNONBLANK – PowerPivot crashed each time I did this.

The first calc I call “PeriodNumber”, which is simply:

PeriodNumber:=MAX(DatePeriods[Number])

This gets the highest value from the DatePeriods table. In other words, if you have selected both 7 and 30 for days, it will include 30 days in the past. You could also make this a bit more complex by using SUM, eg now it would go back 37 days, but I think this would be confusing to a user.

Next, PeriodValue – i.e. which type of Periodic filter do we want.

PeriodValue:=LASTNONBLANK(DatePeriods[Period];1=1)

Now the magic formula:

This formula counts the rows where the date field matches the criteria specified by choosing the slicers. Unfortunately, we can’t use the Period directly in the formula, so I had to wrap it all in a SWITCH statement

InPeriod:=SWITCH ( [PeriodValue]

; “Day”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;DAY)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

; “Month”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;Month)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

; “Year”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;Year)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

)

Very basic: for each row, Count the rows where the Date is before or the Maximum date in the selector, and also where the date plus the number of periods specified is after or on the maximum – this is just a little syntactic sugar as the dateadd can’t be wrapped around the MAX, essentially it’s saying “after MAX minus X Days/Months/Years”

Now, the next piece is likely to be non-performant, you would more likely replace the COUNTROWS with your particular measure, but for my particular cube I wanted to reuse the logic in all my measures without retyping it each time, so I just used the formula below.

Sales:=CALCULATE( SUM([SalesAmount]);

FILTER( FactResellerSales; [InPeriod] > 0)

)

Pretty easy really. Now create a pivot table, add your DatePeriods and DatePeriodSelector as slicers, and you’ll have the workbook shown in Figure 4 below

Nice so far, but what we haven’t covered is a parallel period MTD or YTD. EG if I have selected Feb 2008, my YTD is 1 Jan 2008 to 29 Feb 2008, and my previous YTD is 1 Jan 2007 to 28 Feb 2008.

Let us deal first with the YTD problem. We’ll start by adding a new PeriodName to our source table, and then re-processing the table.

INSERT
INTO
[dbo].[DatePeriods]


([Period]


,[Number]


,[PeriodName])

SELECT
‘YTD’,0, ‘This YTD’

Now, let’s create a new calc to test out our YTD:

YTDcalc:=CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate);

DimDate[CalendarYear] = Max(DatePeriodSelector[CalendarYear])

)

)

*What?!?* I hear *Why aren’t we simply using a TOTALYTD calculation? Put simply, this calc is my template calc, and for financial years we simply use “FiscalYear” instead of calendar year

You will also notice that we don’t have an adjustment for Period number – i.e. I want the YTD for a previous year. Let’s add that in:

YTDcalc:=CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]);

DATEADD(DimDate[FullDateAlternateKey];[PeriodNumber];Year) <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate);

DimDate[CalendarYear] = Max(DatePeriodSelector[CalendarYear]) – [PeriodNumber]

)

)

So this gives us our basic YTD measure, so let’s add it into our switch, and at the same time add in our MTD measure.

InPeriod:=SWITCH ( [PeriodValue]

; “Day”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;DAY)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

; “Month”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;Month)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

; “Year”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]); DimDate[FullDateAlternateKey] <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate[FullDateAlternateKey]); DATEADD(DimDate[FullDateAlternateKey]

;[PeriodNumber]

;Year)

>= Max(DatePeriodSelector[FullDateAlternateKey]))

)

; “YTD”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]);

DATEADD(DimDate[FullDateAlternateKey];[PeriodNumber];Year) <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate);

DimDate[CalendarYear] = Max(DatePeriodSelector[CalendarYear]) – [PeriodNumber]

)

)

; “MTD”

; CALCULATE( CountRows( FactResellerSales ) ;

FILTER ( Values(DimDate[FullDateAlternateKey]);

DATEADD(DimDate[FullDateAlternateKey];[PeriodNumber];Month) <= Max(DatePeriodSelector[FullDateAlternateKey]) );

FILTER ( Values(DimDate);

DimDate[MonthNumberOfYear] = Max(DatePeriodSelector[MonthNumberOfYear]) – [PeriodNumber]

)

)

)

Now to test, let’s insert a couple of additional YTD records.

INSERT
INTO
[dbo].[DatePeriods]


([Period]


,[Number]


,[PeriodName])

SELECT
‘YTD’,1, ‘Last YTD’

UNION
ALL

SELECT
‘YTD’,2, ‘YTD before last’

Process the table, create a new Pivot table, add Sales, add the Slicers for the years and months, and then put the date calc at the top. Et Voila, you have a range of date calcs. Of course, you’ll want to filter these appropriately, to just show the YTD, and then you’ll see the values below. Easy enough for end users to use:



Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images