Forum Discussion
From one record to multiple records (enumerate dates)
- 4 years ago
kazuma6666 This looks to me like Blowout! Blowout! - Microsoft Power BI Community
- 4 years ago
Hi kazuma6666 ,
The answer to your explicit question of how to split out year rows is this: create a new custom column in Power Query and add this as the calculation:
{Date.Year([startDate])..Date.Year([endDate])}Expand the resulting list to new rows and it should duplicate rows for each year covered.
However, the answer to your tacit question is this: You don't need to split out anything into year rows. You need to create a proper calendar table, use this to slice your report for, say, year, then write measures that count the distinct number of memberID's where [startDate] <= MIN(calendar[Date]) and [endDate] >= MAX(calendar[Date]).
There's a bit more to it than this depending on exactly how you want to report, but this is the correct way to leverage Power BI's skills, not by duplicating data rows.
Pete
- 4 years ago
Hello again 🙂
For this example, you won't have a relationship between your calendar table and your fact table.
Your measures should look something like this:
This measure will count a membership as being active within your selected timeframe if it has been active AT ANY POINT within that timeframe:
_noofActiveMemberships_Partial = CALCULATE( DISTINCTCOUNT(factTable[membershipID]), FILTER( factTable, factTable[startDate] <= MAX(calendar[date]) && factTable[endDate] >= MIN(calendar[date]) ) )This measure will only count a membership as being active if it spans the ENTIRE timeframe:
_noofActiveMemberships_Complete = CALCULATE( DISTINCTCOUNT(factTable[membershipID]), FILTER( factTable, factTable[startDate] <= MIN(calendar[date]) && factTable[endDate] >= MAX(calendar[date]) ) )Use the timeframes ([Year], [Month], [Date] etc.) from your calendar table in your visuals along with these measures and it should do what you need.
Pete
- 4 years ago
I think you may need to review your relationships and see if you can reasonably update this table to a MANY type.
This is now essentially a fact table, so would not be expected to operate as the ONE side of any relationships.
Pete
- 4 years ago
Ok, the relationship itself seems fine.
Check that your calendar table spans all the dates in your fact table and vice versa.
Check that you have marked your calendar table as the date table for the model (right-click calendar in field list and go to 'Mark as date table').
Check that your calendar table has been properly created and contains a contiguous list of dates as the [Date] column.
Double-check that you are using the Calendar[Year] column correctly in your slicer.
Beyond this, I would probably need to look at the actual PBIX itself to troubleshoot as could be one of a hundred little variables causing the issue.
Pete
- 4 years ago
TA DA!
Your [SubsDateList] data type is wrong. You have it set as Text, but you need to change it in Power Query to Date type.
When you have finished building your queries in PQ, ALWAYS ALWAYS ALWAYS go through all of your table columns and assign them the correct data type.
For example, your calendar table is almost completely untyped columns.
Go through all of your query columns and, where you see ABC123, click on this little icon and select the correct data type before applying back to the model. It will save you (and possible me!) a billion headaches in future!
Pete
Hi kazuma6666 ,
The answer to your explicit question of how to split out year rows is this: create a new custom column in Power Query and add this as the calculation:
{Date.Year([startDate])..Date.Year([endDate])}
Expand the resulting list to new rows and it should duplicate rows for each year covered.
However, the answer to your tacit question is this: You don't need to split out anything into year rows. You need to create a proper calendar table, use this to slice your report for, say, year, then write measures that count the distinct number of memberID's where [startDate] <= MIN(calendar[Date]) and [endDate] >= MAX(calendar[Date]).
There's a bit more to it than this depending on exactly how you want to report, but this is the correct way to leverage Power BI's skills, not by duplicating data rows.
Pete
Hello again BA_Pete ,
I'm trying to get your second solution working. I have created a date table (using this explanation: https://blog.crossjoin.co.uk/2013/11/19/generating-a-date-dimension-table-in-power-query/). If I understand right, I should use these dates for my CountMembers measure. I am not sure what to use for my measure in this case.
I think what the measure needs to do is look at the input date, and output all the members who have this date between [date begin] and [date end] of their membership. Is that what's needed, or did I miss something?
Is there a specific function I can use that does that?
Thanks in advance for your time!
PS: If you have a link to an explanation/tutorial about this, that would be perfect too!
PPS: Should I create a new topic for this question?
- BA_Pete4 years agoSuper User
Hello again 🙂
For this example, you won't have a relationship between your calendar table and your fact table.
Your measures should look something like this:
This measure will count a membership as being active within your selected timeframe if it has been active AT ANY POINT within that timeframe:
_noofActiveMemberships_Partial = CALCULATE( DISTINCTCOUNT(factTable[membershipID]), FILTER( factTable, factTable[startDate] <= MAX(calendar[date]) && factTable[endDate] >= MIN(calendar[date]) ) )This measure will only count a membership as being active if it spans the ENTIRE timeframe:
_noofActiveMemberships_Complete = CALCULATE( DISTINCTCOUNT(factTable[membershipID]), FILTER( factTable, factTable[startDate] <= MIN(calendar[date]) && factTable[endDate] >= MAX(calendar[date]) ) )Use the timeframes ([Year], [Month], [Date] etc.) from your calendar table in your visuals along with these measures and it should do what you need.
Pete
- kazuma66664 years agoHelper II
Thanks so much for the fast answer. This seems to work perfectly!
- kazuma66664 years agoHelper II
So after more testing, I can say that this works very well.
In my report, I would like to show the results for current year, last year and the year before that.
How would I go about adding the last year's data too?
I tried adding a column in my date table.
OldDateYear-1 = dateadd(PBI_Date_table[Date],-1,year)I then created the same count measure that was described in previous post, using DateYear-1 instead of the original date.
I thought it would show me the data from the previous year, but when I used it in my report, it didn't filter based on the year I chose. It just showed all data for this specific field.
I also tried it in the count measure, but it only accepts column there.CountMembersY-1 = CALCULATE( DISTINCTCOUNT(si_membershipsubscribers[si_membershipsubscriberid]), FILTER( si_membershipsubscribers, si_membershipsubscribers[Valid from].[Date] <= MAX(dateadd(PBI_Date_table[date].[Date],-1,YEAR)) && si_membershipsubscribers[Valid to].[Date] >= MIN(dateadd(PBI_Date_table[date].[Date],-1,YEAR)) ) )So this code throws an error and is not accepted.
Any idea what I should do to get the count of members for the previous year too?
Thanks in advance!
- BA_Pete4 years agoSuper User
Hi kazuma6666 ,
Ok, this may get a little complicated.
If you have an active relationship between your calendar and fact table, it's as easy as this:
_noofActiveMemberships_Complete_PY = CALCULATE( [_noofActiveMemberships_Complete], SAMEPERIODLASTYEAR(calendar[date]) )HOWEVER, as your fact table isn't actually a transactional table (it's a Type 2 Slowly Changing Dimension table), you aren't really going to be able to create a meaningful relationship between your tables.
So, assuming that you have more work to do on this model/report, I think we might need to change our previous approach to allow simpler measure management in the future. This is where we come full-circle to Greg's concept of the 'Blowout'. I call it an 'exploded' table, and it's much simpler to do in Power Query (PQ) than Greg's DAX solution.
Create a new custom column in your fact table in PQ with the following calculation. Call it 'subsDateList', purely because this will help me to reference it later:
List.Transform( { Number.From(si_membershipsubscribers[Valid from]) ..Number.From(si_membershipsubscribers[Valid to]) }, each Date.From(_) )You will now have a column full of nested lists. Expand this column using the button in the column header that looks like two arrows pointing away from each other. This will expand each SCD record into a new row for every date between [Valid from] and [Valid to].
At this point, you will need to decide the maximum relative time that you are going to report into the past. Firstly, so that you can immediately reduce the large number of rows you now have and, secondly, to prevent the table growing indefinitely until it's a homologous data blob that takes over the world.
By the sounds of your YoYoY requirement, you will only want to keep data from January 1st, 2 years ago. Do this by filtering on [subsDateList] as required.
Once you've got this table, you can relate calendar[date] to factTable[subsDateList], and Power BI will aggregate your measures automatically in your visuals.
Don't be too scared if there's hundreds of thousands, or even low-millions of rows in this is table. I've created entire models based on many exploded SCD tables and, while they take up more HDD and take longer to refresh, once they're loaded the reports perform remarkably well (just avoid using iterator functions over them if you can!).
So, your new measures will be:
// New number of members within period _noofMembers = DISTINCTCOUNT(si_membershipsubscribers[si_membershipsubscriberid]) // New number of members prior year to selected period _noofMembersPY = CALCULATE( [_noofMembers], SAMEPERIODLASTYEAR(calendar[date]) )Sorry it goes on a bit, hope it makes sense 🙂
PS: as an aside, I would personally recommend shortening your table and column names and making them clearer to read. For example, I would change 'si_membershipsubscribers' to 'factMemSubs', and [si_membershipsubscriberid] to [memSubID]. This will make it easier to write and manage M/DAX code going forward.
Pete