Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Join us for an expert-led overview of the tools and concepts you'll need to become a Certified Power BI Data Analyst and pass exam PL-300. Register now.

Reply
adamlang
Helper III
Helper III

New, Returning, and Lost Clients but based on multiple fact tables

Hi there,

 

Within in pbix file linked below, I have incorporated a version of the SQLBI Pattern for New and Returning customers. I'm using the dynamic by category variation because I need to segment by Project.

 

https://www.dropbox.com/scl/fo/yywvidb85p6vgk1buw3gh/h?rlkey=9nkcp3zam0a4mziynl5hvvdek&dl=0

 

The pattern currently uses a single fact table - Project_Enrollments, however for various reasons (mainly because we're often not able to close an enrollments when we stop engaging with a client) I need to change this so that instead the pattern looks across three fact tables, which all using different formatting:

 

- Visits 

- Appointments

- Workshop attendances

 

Instead of counting the sum of sales, the # measure in this version should count the number of interactions across those three tables for each clients. I've set up a measure to do the basic count - [Total Count of Activities], its in table 1.

 

The pattern is far beyond my Dax skills to modify, I’ve only be using Power BI for six months or so, and it's right past my current limits.

 

I think that's the key information, let me know if I've missed anything. I think this is a question much simpler to ask than it is to answer.

 

Thanks for any help - it will save us from regularly having to do this analysis manually in excel.

 

Adam

5 REPLIES 5
lbendlin
Super User
Super User

Your request is too detailed.  Boil it down to the essential ask and prepare sample data that covers this completely (but not more).


Want faster answers? https://community.fabric.microsoft.com/t5/Desktop/How-to-Get-Your-Question-Answered-Quickly/m-p/1447...

Thanks @lbendlin ,

 

That's useful to know. I'm new at this, and its not really my background - so still trying to work out the best way to learn and ask for help. I appreciate you taking the time to say I wasn't asking in the right way. I think my ask is maybe both complex, and uses an unusual data model?

 

My main general problem is, most of the Dax examples/ patterns I see are based on a sales database, often with a single sales table (sometimes an additional orders tables). The CRM I'm working with has a quite different structure to that and I'm struggling to adapt - here I've hit a brick wall. I think I was hoping someone may have adapted the SQLBI pattern (I should have provided the link previously) in a similar way (i.e. using multiples fact tables rather than just one), may be able to provide a bit of a modified template. I wasn't really expecting anyone to have the time to work out a whole new pattern just for this scenario. 

 

One simple solution is maybe to merge the three activity fact tables into one table (the pattern should then be able to work with that single table) but the general advice seems to be not to do that. In my case they contain quite different data so it would be a bit of a mess I fear. Maybe this could be done in a virual table, I'm not sure if possible and what the implications might be of that approach.

 

My test files do contain two tables that are not immediately relevant - Outcomes and Assessments, but in a future version of my churn analysis I will need to bring them in. Once I've got a general approach to using this pattern across the three activities tables, I think it'll be fairly straightforward to add the two further tables.

 

I've already adapted the SQLBI template pattern to use a single table in my model (Project Enrolments) but that won't meet the business requirement, the pattern needs to scan the three activities tables to identify each type of  customer during a period.

 

Essentially, in the template pattern, there are three types of measure, the first two are causing the difficulty:

 

1. There are three measures that identify the date in which each customer was new, lost, temporarily lost, and recovered within each period (say on a monthly basis) in my model this needs to be done at a project level, so a customer can be 'new' each time they attend (i.e. participate in either a visit, appointment, or workshop) linked to a project for the first time.

 

2. Then using these date measures, a second set of measures counts the clients that fall into each definition during the period.

 

3. A third will also use this segmentation to count the number of activities the clients have attended in total. This part is simple as it just a case of using an existing measure that simply counts the activities across the three tables.

 

Note: The project enrollment table is unique in my model in that it contains both a start and a end date (I had some help on this forum to adapt the template for this). The activities tables however each only have the one date:
 
'Visits' [VisitDate]
'Appointments'[StartDate]
'Workshop_Attendances'[WorkshipDate]

 

 

For example, take the new Client date measure:

 

Date New Client Enrolment =
CALCULATE (      
    MIN ( Project_Enrollments[Start Date]), -- The first enrollment is the MIN of enrolment Date
    ALLEXCEPT (
        Project_Enrollments,                 -- ignoring filters
        Project_Enrollments[URN],    -- other than client
        Contact,
        'Pre_Project'[Project Name]    -- and the Project Name
    )
)
 
Rather than just looking at the single Project Enrollments table, I need it to look across the three tables (Visits, Appointments, and Workshops). So the min date for each client needs to be across the three tables and the list of projects.
 
It then gets a bit more complex with the other measures, e.g. the measure Date Lost Client below, but once I have a general approach I think I can adapt the rest of the pattern.
 
Date Lost Client =
VAR MaxStartDate = CALCULATE(                               -- The last enrolment is the MAX of Enrollment Start Date and if present the Enrolment end date
MAX(Project_Enrollments[Start Date]),                   -- in the current time period (set by the calling measure)
ALLEXCEPT(                                                  
Project_Enrollments,                                       -- ignoring any filter
Project_Enrollments[URN],      -- other than Customer
Contact,
'Pre_Project'[Project Name]                              -- and Project Name
)
)

VAR MaxEndDate = CALCULATE(
MAX(Project_Enrollments[Finish Date]),
ALLEXCEPT(
Project_Enrollments,
Project_Enrollments[Finish Date],
Contact,
'Pre_Project'[Project Name]
)
)

VAR MaxDate = IF(
NOT ISBLANK(MaxEndDate),
MAX(MaxStartDate, MaxEndDate),
MaxStartDate
)

VAR Result = IF(
NOT ISBLANK(MaxDate),
EOMONTH(MaxDate, 2), -- two months added to the most recent enrolment start or finish date  (end of month)
BLANK()
)

RETURN Result
 
Many thanks,


Adam  

CRM solutions are usually network graphs, rather than hierarchy star schemas. Trying to replicate them in Power BI for more than a small subset will be as painful as you describe. The closest you can get to that is by using Qlik and their associative model.

@lbendlin 

 

So I think I've solved this using a new virtual tabel that joins the three fact tables into one so that the customer churn pattern can easily have all the data across the three tables without modifing the code much. Not sure if this is best practice but it apears to do the trick, and isn't massivly slow in my actual model.

 

I didn't want to summerise any of the data just a straight copy of the existing tables with select columns and a couple of simple filters. The Dax I used is:

 

Merged Activities =
UNION (
    SELECTCOLUMNS (
        Visits,
        "URN", Visits[URN],
        "Dates", Visits[VisitDate],
        "Project", Visits[Project],
        "Activity Type", Visits[Activty type]
    ),
    CALCULATETABLE (
        SELECTCOLUMNS (
            Appointments,
            "URN", Appointments[URN],
            "Dates", Appointments[StartDate],
            "Project", Appointments[Project],
            "Activity Type", Appointments[Activity Type]
        ),
        Appointments[Status] = "Attended"
            || Appointments[Status] = "Booked"
            || Appointments[Status] = ""
    ),
    CALCULATETABLE (
        SELECTCOLUMNS (
            Workshop_Attendances,
            "URN", Workshop_Attendances[URN],
            "Dates", Workshop_Attendances[WorkshopDate],
            "Project", Workshop_Attendances[Project],
            "Activity Type", Workshop_Attendances[Activity type]
        ),
        'Workshop_Attendances'[Status] = "N/A"
            || Workshop_Attendances[Status] = "Attended"
    )
)

 

Is this the right code, I'd like to check its correctly optimisised and is mantaining the data data lineage?

 

The reference guide for SELECTCOLUMNS suggests that any expression used in the new columns might remove the lineage, does that include the filters I've used in CALULATETABLE? As I say it seems to be maintaining the relationships as I would expect.

 

I've uploaded an updated version of the Test model.pbix file to the dropbox link shared eariler.

 

Thanks,

 

Adam

to break the lineage you would have to add something to the fields, like  

 

"Dates", Workshop_Attendances[WorkshopDate]+0,

 

Instead of the multiple || you could use IN  but that's just syntax sugar. No performance difference.

Helpful resources

Announcements
Join our Fabric User Panel

Join our Fabric User Panel

This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.

June 2025 Power BI Update Carousel

Power BI Monthly Update - June 2025

Check out the June 2025 Power BI update to learn about new features.

June 2025 community update carousel

Fabric Community Update - June 2025

Find out what's new and trending in the Fabric community.