Search code examples
filterpowerbidaxmeasurecartesian-product

DAX: Unwanted cartesian product lines?


Please help to calculate/understand properly lastDate and rankDate measures for following simplified example (download):

enter image description here

Desired result:

enter image description here

Reality (incorrect subtypes):

enter image description here

Why relationship is broken?

How to avoid this cartesian product lines?

My Measure (I commented workaround, because it's kind of postfilter, not prefilter):

rnkDate = 
VAR t =
    CALCULATETABLE(
        VALUES(tstTable[Date]),
        REMOVEFILTERS(tstTable[Date])
    )
RETURN 
    //IF( MAX(tstTable[Amount])<>BLANK(), // WORKAROUND To hide unwantedd rows
        RANKX(
            t,
            LASTDATE(tstTable[Date])
        )
    //)

P.S. Mess happens only if I use fields from dimensional table dimType[Type] (within one table everything is Ok):

enter image description here


Solution

  • The problem is that the query generated by Power BI performs the cartesian product and filers the result by checking the result of the measure.

    in our case is something similar to

    SUMMARIZECOLUMNS(
      'dimType'[Type],
      'tstTable'[subType],
      'tstTable'[Date],
      "MinAmount", CALCULATE(MIN('tstTable'[Amount])),
      "lastDate", 'tstTable'[lastDate],
      "rnkDate", 'tstTable'[rnkDate]
    )
    

    SUMMARIZECOLUMNS doesn't use relationships when iterating on different tables, it applies them when evaluating the measures. There is an article explaining what is the equivalent DAX code executed by SUMMARIZECOLUMNS

    Introducing SUMMARIZECOLUMNS

    the problem is that RANKX evaluated on an empty table retuns 1. This can be seen executing this on dax.do

    EVALUATE
    VAR t =
        FILTER ( ALL ( 'Date'[Date] ), FALSE )
    RETURN
        { RANKX ( t, [Sales Amount] ), CALCULATE ( [Sales Amount], t ) }
    

    so the solution is to first check that the table t is not empty, which is the reason because the workaround that you implemented solved the issue

    lastDate = 
    IF( NOT ISEMPTY(tstTable), // checks fact table in particular context
        CALCULATE(
            LASTDATE(tstTable[Date]),
            REMOVEFILTERS(tstTable[Date])
        )
    )
    

    rnkDate = 
    VAR t =
        CALCULATETABLE(
            VALUES(tstTable[Date]),
            REMOVEFILTERS(tstTable[Date])
        )
    RETURN 
        IF( NOT ISEMPTY(tstTable),
            RANKX(
                t,
                LASTDATE(tstTable[Date])
            )
        )