DAX “IN” Operator

It’s hard to keep a secret nowadays. Although Microsoft has not officially announced it, people have discovered on their own that DAX has a new IN operator. Let me explain why the Power BI team have not made this public yet.

  • The IntelliSense work has not completed yet so although a DAX expression containing the new syntax will be accepted by the engine, it will be red squiggly underlined in the DAX editor.

redsquigglyinoperator

  • Users cannot use the new syntax directly as a setfilter argument to the CALCULATE family of functions. So the following syntactic shortcut is not yet accepted by the DAX engine.
CALCULATE([Total Sales], 'Product'[Color] IN { "Red", "Green" }) // Not working right now.

You have to use the full version instead:

CALCULATE(
    [Total Sales],
    FILTER(ALL('Product'[Color]), [Color] IN { "Red", "Green" })
)

If you are unfazed by these limitations which should be addressed early next year, feel free to enjoy the new convenience in DAX today.

'Product'[Color] IN { "Red", "Green" }

or

('Product'[Color], 'Customer'[Country]) IN { ("Red", "Russia"), ("Italy", "Green") }

By the way, since DAX is a functional language, there is an equivalent new function CONTAINSROW:

CONTAINSROW({ "Red", "Blue" }, 'Product'[Color])

or

CONTAINSROW(
    { ("Red", "Russia"), ("Italy", "Green") },
    'Product'[Color],
    'Customer'[Country]
)

The examples above show off not just the IN operator, but also a new way to construct a table expression from a list of values or tuples. As with other DAX functions and operators, the new syntax is fully composable as illustrated in the following examples of valid DAX expressions:

'Product'[Color] IN VALUES('Product2'[Color])
{ VALUES('Date'[Year]) - 1, VALUES('Date'[Year]), VALUES('Date'[Year]) + 1 }

warningAlthough the following syntax is also accepted by the DAX engine today, it is not by design so you should only use curly braces {} instead of parentheses () to enclose a list of values or tuples, the latter of which should be enclosed in ().

'Product'[Color] IN ( "Red", "Green" ) // Do not do this. Replace () by {} instead.

 

DAX “IN” Operator

15 thoughts on “DAX “IN” Operator

    1. Yes, you can use the IN operator in RLS expressions. Since there is no IntelliSense there, you don’t even see the red squiggly. Of course IN is so new that it’s only available in Power BI, not PowerPivot or Analysis Services.

      Liked by 1 person

  1. Mr. Wang regarding your answer to Tony on ‘NOT’. Does this mean the entire expression is deMorgan’ed like is the case with SQL’s NOT IN operator (that would mean that a single BLANK value would make the entire predicate fail)?

    Like

    1. There are two separate operators involved here: the IN operator and the NOT operator. IN operator has precedence over NOT operator, so NOT [Column] IN { Value1, Value2 } is treated as NOT ([Column] IN { Value1, Value2 }).
      Not sure about the BLANK value question. In SQL, NULL means unknown therefore NULL anywhere renders the entire predicate unknown. But in DAX, BLANK compares equal to FALSE. DAX BLANK does not mean unknown as in SQL but casts to FALSE when converted to Boolean.

      Like

  2. sam says:

    If I need to replicate this in Excel 2013 what would be the equivalent expression

    FILTER(VALUES(D[DEPT]),D[DEPT] IN
    SELECTCOLUMNS(
    FILTER(LOGNM,LOGNM[USERID]=[mUserNm]),
    “DEPT”,LOGNM[DEPT]))

    where [mUserNm] is MID([mUser],FIND(“\”,[mUser])+1,LEN([mUser]))
    and [mUser] is USERPRINCIPALNAME()
    D is a fact table containing Column DEPT
    LOGNM is a user name table with Columns User ID and DEPT

    Like

    1. In Excel 2013, you use the following DAX formula. USERPRINCIPALNAME is a new function, I don’t think it exists in older version of PowerPivot, but USERNAME works from the beginning.
      FILTER(
      VALUES(D[DEPT]),
      COUNTROWS(
      FILTER(
      LOGNM,
      LOGNM[DEPT] = D[DEPT] && LOGNM[USERID] = [mUserNm]
      )
      ) > 0
      )

      Like

  3. Raviteja G says:

    Hi,
    my below query throwing error syntax for IN is incorrect:
    Please let me know if i am missing anything.
    evaluate (
    filter(
    summarize(
    addcolumns(‘sales’
    ,”col1″ ,
    ,”col2″)
    ,’sales’ row_id
    ,’sales’ col5
    ,’sales’ col6
    ), date in{“2018″,”2019″},segment=”ABC”, opp in {“1″,”2″,”10”}
    )
    )

    Like

  4. Dongyang says:

    Hi Jeffrey,
    thanks for your detailed introduction, especially those posts about logical plan/vertipaq operators of DAX, which helps me know DAX better.

    The post address is http://mdxdax.blogspot.com/2012/03/dax-query-plan-part-3-vertipaq.html.

    But there’re some concepts still confusing me, like “natural semi-join”. (I understand natural join and semi join which are common concepts of relational algebra, but I cannot image the combination of them).

    And related to “natural semi-join”, the role of SemijoinCols played in the Scan_Vertipaq operator also baffled me a lot, since in most cases, SemijoinCols would be eliminated during the simplification process. I guess the elimination happened when the filter is just simple column filter, such that filtering can be implemented by sigma operation rather than complex semi-join. But when filters are complex, like filters of arbitrary shape or CALCULATETABLE as a filter, how does SemijoinCols make a difference?

    Feel sorry for bothering you with such long questions…

    Like

  5. By natural semi-join, I meant how the join columns are determined. In DAX, when relation R1 semijoins with relation R2, the join columns must have identical lineages to the underlying tables in the model, e.g. DimDate[Date] joins with DimDate[Date], Fact[OrderDate] joins with Fact[OrderDate]. In constrast, DimDate[Date] cannot join with Fact[OrderDate] in this situation. In a sense, DAX semijoin is always a self-join.
    Whether a semijoin can be reduced (simplified) has nothing to do with what the join columns are. Regardless of the complexity of the DAX expression of the filter relation, DAX Engine can reduce semijoin to a multi-column IN clause by materializing the filter expression into a set of tuples. A semijoin cannot be reduced when the filter expression depends on external columns in the evaluation context, in other words, when the filter expression is dynamic instead of static.

    Liked by 1 person

Leave a comment