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

Calling all Data Engineers! Fabric Data Engineer (Exam DP-700) live sessions are back! Starting October 16th. Sign up.

Reply
locka
Helper II
Helper II

issue count number of matching values.

I've written the following below for our audit table which has the potential to have multiple audit entries per faultid per day.  I wanted to count the number of tickets that where active per day.  This  Work's out which is last audit entry for that day.   This is table can be filtered by a date period & agent or team slicers connected to a disconnected date tables.   This is then filters down list of results showing only the last audit entry of faultid.

As it stand this works fine if used in a table row.  If I add this to filter context of table (Only a  table) where it equals 1 then it will filter down the correct number of rows. 

However Id like to count the number of rows, rather than it work out if each row is correct.  I've tried to do SumX or similar which will not work I've also tried Summrizecolumns etc.

I'm guessing it creating a table value and not scalar value.  Id like to end up with measure rather than table or caculated column

_Active =
--Max and Min dates selected from the slicer
VAR MaxDate = MAX('@Dim_Date'[Date])
VAR MinDate = MIN('@Dim_Date'[Date])
 
VAR Max_Date =
CALCULATE(
    MAX('FACT_FaultAssignmentHistory'[FromDateTime]),
        FILTER(
            ALLSELECTED('FACT_FaultAssignmentHistory'),
                'FACT_FaultAssignmentHistory'[Faultid] = MAX('FACT_FaultAssignmentHistory'[Faultid])
                && 'FACT_FaultAssignmentHistory'[FromDate]  <= MaxDate
                && ('FACT_FaultAssignmentHistory'[ToDate] >= MinDate
                        || (ISBLANK('FACT_FaultAssignmentHistory'[ToDate])
                                && ISBLANK(RELATED(FACT_Faults[FinishedDate]))
                        )
                    )
        )
        , FACT_FaultAssignmentHistory[AssignedAgentID] IN VALUES('@DIM_Agent'[AgentID]) || FACT_FaultAssignmentHistory[AgentTeam] IN VALUES('@DIM_Teams'[AgentTeam])
)

VAR Result =

CALCULATE(
    COUNTROWS('FACT_FaultAssignmentHistory'),
        FILTER(
            'FACT_FaultAssignmentHistory',
            FORMAT('FACT_FaultAssignmentHistory'[FromDate],"Short Date") = FORMAT(Max_Date,"Short Date") &&
            FORMAT('FACT_FaultAssignmentHistory'[FromDateTime],"ttttt") = FORMAT(Max_Date,"ttttt") &&
            NOT RELATED(DIM_Statuses[Status]) IN {"Rejected","Cancelled"}
        )
)

RETURN
    Result
12 REPLIES 12
tayloramy
Community Champion
Community Champion

Hi @locka,

 

 

You’re very close. The main things slowing you down / blocking a scalar count are:
- using FORMAT(...) in row filters (it forces string scans and kills indexes),
- relying on row-by-row comparisons instead of a single, set-based filter,
- applying disconnected slicers implicitly instead of mapping them with TREATAS.

Below is a drop-in measure that:
1) works against your condensed FACT_FaultEndOfDayAssignment table,
2) supports disconnected date / agent / team slicers, and
3) returns a scalar count (so you can use it in cards, KPIs, or filters).

 

Active Tickets (range) - counts tickets that overlap the selected date range, filtered by the disconnected slicers:

Active Tickets (range) :=
VAR MinDate = MIN ( '@DIM_Date'[Date] )
VAR MaxDate = MAX ( '@DIM_Date'[Date] )
RETURN
CALCULATE (
    COUNTROWS ( FACT_FaultEndOfDayAssignment ),
    // overlap: [From] <= Max AND COALESCE([To], +infinity) >= Min
    KEEPFILTERS (
        FILTER (
            FACT_FaultEndOfDayAssignment,
            FACT_FaultEndOfDayAssignment[FromDatetime] <= MaxDate
                && COALESCE ( FACT_FaultEndOfDayAssignment[ToDatetime], DATE ( 9999, 12, 31 ) ) >= MinDate
        )
    ),
    // map disconnected slicers
    TREATAS ( VALUES ( '@DIM_Teams'[AgentTeam] ), FACT_FaultEndOfDayAssignment[AgentTeam] ),
    TREATAS ( VALUES ( '@DIM_Agent'[AgentID] ),   FACT_FaultEndOfDayAssignment[AssignedAgentID] ),
    // optional status exclusion (faster than IN/FORMAT)
    KEEPFILTERS (
        FILTER (
            FACT_FaultEndOfDayAssignment,
            FACT_FaultEndOfDayAssignment[Status] <> "Rejected"
                && FACT_FaultEndOfDayAssignment[Status] <> "Cancelled"
        )
    )
)

Active Tickets (per day) — same logic, but when you put it on a daily axis it evaluates per the day in context (no strings/FORMAT):

Active Tickets (per day) :=
VAR DayStart = SELECTEDVALUE ( '@DIM_Date'[Date] )
VAR DayEnd   = DayStart
RETURN
CALCULATE (
    COUNTROWS ( FACT_FaultEndOfDayAssignment ),
    KEEPFILTERS (
        FILTER (
            FACT_FaultEndOfDayAssignment,
            FACT_FaultEndOfDayAssignment[FromDatetime] <= DayEnd
                && COALESCE ( FACT_FaultEndOfDayAssignment[ToDatetime], DATE ( 9999, 12, 31 ) ) >= DayStart
        )
    ),
    TREATAS ( VALUES ( '@DIM_Teams'[AgentTeam] ), FACT_FaultEndOfDayAssignment[AgentTeam] ),
    TREATAS ( VALUES ( '@DIM_Agent'[AgentID] ),   FACT_FaultEndOfDayAssignment[AssignedAgentID] ),
    KEEPFILTERS (
        FILTER (
            FACT_FaultEndOfDayAssignment,
            FACT_FaultEndOfDayAssignment[Status] <> "Rejected"
                && FACT_FaultEndOfDayAssignment[Status] <> "Cancelled"
        )
    )
)

Notes
- No FORMAT calls (huge perf win).
- TREATAS cleanly applies your disconnected slicers.
- Your Power Query “end-of-day” condensation means each FaultId has at most one row per day, so a COUNTROWS after set filters is both correct and fast.
- Keep your existing CROSSFILTER only if you genuinely need to override a relationship; otherwise let the model relationships do the work.

why this is faster + reliable

  1. Events-overlap pattern
    You want tickets that were “active” at any point in the selected window. The canonical test is: [From] <= WindowMax AND COALESCE([To], +infinity) >= WindowMin. That’s what the FILTER(...FromDatetime <= MaxDate && COALESCE(ToDatetime, 9999-12-31) >= MinDate...) does. See: TREATAS, CALCULATE, FILTER.
  2. Disconnected slicers done right
    Because your date/agent/team slicers are disconnected (by design), you must map their selections to the fact table. TREATAS projects the selected values onto the fact columns efficiently, without building large intermediate tables or resorting to SUMMARIZECOLUMNS. Docs: TREATAS.
  3. Avoid FORMAT in filters
    FORMAT(DateTime, "Short Date") turns an indexed datetime into a string, defeating storage engine optimizations and forcing row-by-row scans. Using native comparisons on the actual Date/DateTime columns is both more correct (no locale surprises) and much faster.
  4. Keep filters narrow and composable
    The KEEPTFILTERS(FILTER(...)) pattern scopes the comparison to the fact table only, letting the rest of your model’s filters flow normally. That’s usually faster than wrapping everything in a wide CALCULATE/FILTER over ALLSELECTED(...).
  5. Totals vs daily
    - Place Active Tickets (per day) on a line chart or matrix by Date for daily counts.
    - Use Active Tickets (range) in cards/KPIs to get the total distinct active rows across the whole selection window.
  6. If you ever remove the “EOD condensation”
    You can still get the “last audit entry per day per FaultId” in DAX with a virtual table, but keep it for Power Query if you can—it’s cheaper to pre-reduce the cardinality once than re-compute it at query time.

Optional tidy-ups

- If you currently use CROSSFILTER(..., Both) in the measure, prefer a proper relationship with the minimal necessary direction. Only keep CROSSFILTER if you’re overriding an inactive/special case.
- If your Date slicer range is large, consider capping the MaxDate to TODAY() (or a data watermark) to prevent accidental “open-ended” windows.

If you’d like, share a tiny PBIX (anonymized) and I can sanity-check the model relationships and confirm you don’t need the CROSSFILTER at all.


If you found this helpful, consider giving some Kudos. If I answered your question or solved your problem, mark this post as the solution.

v-sgandrathi
Community Support
Community Support

Hi @locka,

 

Thank you for the thorough explanation. This helps clarify why I was encountering query limits, and the suggestions for using variables and TREATAS are very useful.
Please reachout to us for more queries
Thank you and continue using Microsft Fabric Community Forum.

v-sgandrathi
Community Support
Community Support

Hi @locka,

 

This behavior is normal for audit history tables. If several records have the same From Date/Time, the one with a To Date indicates a closed event, while the one without a To Date is the most recent and currently active record.

For performance, SUMX works but can be slow since it processes each row individually. A better option is to use a calculated column, such as ActiveFlag, and aggregate with SUM, or change the logic to focus only on the latest entry for each record. This will help improve speed by reducing row-by-row calculations.

 

Thank you.

Hi @locka,

 

Just wanted to follow up and confirm that everything has been going well on this. Please let me know if there’s anything from our end.
Please feel free to reach out Microsoft fabric community forum.

Hi @locka,

 

We strive to make sure every issue gets addressed completely. Still facing issues? Just reply here and we’ll be right with you.

Thank you for being a valued part of the community.

 

Hi @locka,

 

Just looping back one last time to check if everything's good on your end. Let me know if you need any final support happy to assist if anything’s still open.

 

Thank you.

Hi thanks for checking in.

I now have a working measure.  Which now works out which tickets area\where active over a period chosen. However I believe it's the reason I'm now getting a lot query exceeds.   I'm wondering how best to make the dax more efficient? 

FACT_FaultEndOfDayAssignment is now a condensed version of faultassignment history table.  In powerquery its only keeping entry with latest Fromdate per day.  So now it can only ever have one Faultid & Fromdate the same. Although a ticket can have the same Todate & Fromdate.

Both my DateTable along with my AgentTable & AgentTeamTable have to be disconnected tables as they are linked to multiple fields some in the same table which refer to different dates\people. As agent can create a ticket but a different agent resolve it etc.  So this allows use to only need to pick one date, team, agent and then depending the visual is filter accordingly.

VAR MaxDate =
    MAX ( '@DIM_Date'[Date]  ) // Newest date in the date table.

VAR MinDate =
    MIN (  '@DIM_Date'[Date]  ) // Earliest date in the date table.

VAR Result =
  CALCULATE(
        COUNTROWS(FACT_FaultEndOfDayAssignment),
        'FACT_FaultEndOfDayAssignment'[FromDatetime] <=MaxDate
        && ('FACT_FaultEndOfDayAssignment'[ToDatetime] >=MinDate
            || ISBLANK('FACT_FaultEndOfDayAssignment'[ToDatetime]))
            ,FACT_FaultEndOfDayAssignment[AgentTeam] IN VALUES('@DIM_Teams'[AgentTeam])
            ,FACT_FaultEndOfDayAssignment[AssignedAgentID] IN VALUES('@DIM_Agent'[AgentID])
            ,CROSSFILTER(FACT_FaultEndOfDayAssignment[Faultid],FACT_Faults[Faultid],both)
    )
   
RETURN
    Result
locka
Helper II
Helper II

Ok if I use my original measure.  The sum it via a second measure using 

SUMX(FACT_FaultAssignmentHistory,[_ActiveCount])

That works fine but I'm getting some audit entries which have the exactly same from date and time. Each one has one with To date and No end date yet as it has'nt been update with any new audit entries just trying to figure out if that's correct and which one is latest entry.

However it does seem to be very slow at caculating it.  Which means that codes is little efficient.
sivarajan21
Post Prodigy
Post Prodigy

Hi @locka 

 

PFA 

Active Count = 
VAR MaxDate = MAX('@Dim_Date'[Date])
VAR MinDate = MIN('@Dim_Date'[Date])

RETURN
COUNTROWS(
    FILTER(
        'FACT_FaultAssignmentHistory',
        VAR MaxFromDate = 
            CALCULATE(
                MAX('FACT_FaultAssignmentHistory'[FromDateTime]),
                ALLEXCEPT('FACT_FaultAssignmentHistory', 'FACT_FaultAssignmentHistory'[FaultId]),
                'FACT_FaultAssignmentHistory'[FromDate] <= MaxDate
                && ('FACT_FaultAssignmentHistory'[ToDate] >= MinDate 
                    || ISBLANK('FACT_FaultAssignmentHistory'[ToDate]))
            )
        RETURN
        FORMAT('FACT_FaultAssignmentHistory'[FromDateTime], "tttt") = FORMAT(MaxFromDate, "tttt")
        && NOT RELATED('DIM_Statuses'[Status]) IN {"Rejected", "Cancelled"}
    )
)

Please let me know if this is what you need
Best regards,

Thanks for the reply
However I now get duplicate faultid  I modifed your code slightly to give filter the teams selected, but what I see is duplicates of the faultid.  So it's not picking the latest From datatime, which infaltes the count.  This what Ive been using: -

_Active Count =
VAR MaxDate = MAX('@Dim_Date'[Date])
VAR MinDate = MIN('@Dim_Date'[Date])

RETURN
COUNTROWS(
    FILTER(
        'FACT_FaultAssignmentHistory',
        VAR MaxFromDate =
            CALCULATE(
                MAX('FACT_FaultAssignmentHistory'[FromDateTime]),
                ALLEXCEPT('FACT_FaultAssignmentHistory', 'FACT_FaultAssignmentHistory'[FaultId]),
                'FACT_FaultAssignmentHistory'[FromDate] <= MaxDate
                && ('FACT_FaultAssignmentHistory'[ToDate] >= MinDate
                    || (ISBLANK('FACT_FaultAssignmentHistory'[ToDate])
                            && ISBLANK(RELATED(FACT_Faults[FinishedDate]))
                        )
                    ), FACT_FaultAssignmentHistory[AssignedAgentID] IN VALUES('@DIM_Agent'[AgentID]) || FACT_FaultAssignmentHistory[AgentTeam] IN VALUES('@DIM_Teams'[AgentTeam])
            )      
        RETURN
         FORMAT('FACT_FaultAssignmentHistory'[FromDateTime], "tttt") = FORMAT(MaxFromDate, "tttt")
        && NOT RELATED('DIM_Statuses'[Status]) IN {"Rejected", "Cancelled"}
    )
)
tayloramy
Community Champion
Community Champion

Hi @locka 


Your current measure returns a table-like result per row and relies on FORMAT to compare dates/times. That works in a table visual but makes it hard to produce a single scalar count. Also, FORMAT converts values to text, which hurts performance and can change filter behavior. The fix is to:
1) compute a row-level "is last per FaultId per day" flag as a measure, and
2) SUM it up over the fact table restricted to the disconnected date range and Agent/Team selections.

Quick solution
Create two measures. The first returns 1 only for the last audit row per FaultId per day within the current filter context. The second counts those rows for the selected date range and Agent/Team filters.

-- 1) Helper measure: returns 1 for the last audit row for a FaultId on a given day
Active Row Flag :=
VAR d = MAX ( 'FACT_FaultAssignmentHistory'[FromDate] )
VAR f = MAX ( 'FACT_FaultAssignmentHistory'[Faultid] )
VAR lastAuditThisDayForFault :=
    CALCULATE (
        MAX ( 'FACT_FaultAssignmentHistory'[FromDateTime] ),
        ALLEXCEPT (
            'FACT_FaultAssignmentHistory',
            'FACT_FaultAssignmentHistory'[FromDate],
            'FACT_FaultAssignmentHistory'[Faultid]
        )
    )
VAR isAllowedStatus =
    NOT ( RELATED ( DIM_Statuses[Status] ) IN { "Rejected", "Cancelled" } )
RETURN
IF (
    MAX ( 'FACT_FaultAssignmentHistory'[FromDateTime] ) = lastAuditThisDayForFault
        && isAllowedStatus,
    1, 0
)
-- 2) Final scalar measure: counts active tickets (last audit per FaultId per day)
Active Tickets :=
VAR DateFilter = VALUES ( '@Dim_Date'[Date] )
-- Push the disconnected date selection onto the fact table
VAR FactInDateRange :=
    CALCULATETABLE (
        'FACT_FaultAssignmentHistory',
        TREATAS ( DateFilter, 'FACT_FaultAssignmentHistory'[FromDate] )
    )
-- Optional: restrict to selected Agents or Teams if either slicer is used
VAR FactFiltered :=
    FILTER (
        FactInDateRange,
        (
            ISBLANK ( SELECTEDVALUE ( '@DIM_Agent'[AgentID], BLANK () ) )
            && ISBLANK ( SELECTEDVALUE ( '@DIM_Teams'[AgentTeam], BLANK () ) )
        )
        || 'FACT_FaultAssignmentHistory'[AssignedAgentID] IN VALUES ( '@DIM_Agent'[AgentID] )
        || 'FACT_FaultAssignmentHistory'[AgentTeam] IN VALUES ( '@DIM_Teams'[AgentTeam] )
    )
RETURN
CALCULATE ( SUMX ( FactFiltered, [Active Row Flag] ) )

Notes and gotchas
- Do not use FORMAT for date/time comparisons; keep them as true date/time values for correct filtering and better performance. See DAX CALCULATE and filter context behavior: CALCULATE, and date filtering with TREATAS: TREATAS.
- ALLEXCEPT retains the grouping by FaultId and FromDate so MAX finds the last FromDateTime within that group: ALLEXCEPT.
- SUMX over the filtered fact table yields a scalar, so you can use it in cards and KPIs.
- If you prefer, you can replace the Agent/Team block with a strict requirement that both slicers must match by removing the first OR branch.

 

If you found this helpful, consider giving some Kudos. If I answered your question or solved your problem, mark this post as the solution.

Thanks for the reply.

I had to tweak first measure around isnotallowedstatus but I can live with that for now.  However second measure just produces blank.  The first measure is giving me rows which have 1 against them. 

Looking at the second measure I think its because it needs to be looking at tickets which have a date in that period as in they must have been open on that day.  So for example yesteday no tickets have that date so they are blank.   What it needs to be able to do is have tickets which were open at somepoint over that period: -

 

  1.  Where created on or before that date and either have a "to"date of that day or Later than that date\blank

Where as I think your measure is assuming it all tickets just have that from date where its quite conceivable that it has an earlier date than that as well. 

Also tried to breakdown the value by team in matrix and that made no difference it just gave me a total.  Is that not possible?

Helpful resources

Announcements
FabCon Global Hackathon Carousel

FabCon Global Hackathon

Join the Fabric FabCon Global Hackathon—running virtually through Nov 3. Open to all skill levels. $10,000 in prizes!

FabCon Atlanta 2026 carousel

FabCon Atlanta 2026

Join us at FabCon Atlanta, March 16-20, for the ultimate Fabric, Power BI, AI and SQL community-led event. Save $200 with code FABCOMM.

Top Solution Authors
Top Kudoed Authors