LOOKUPVALUE DAX Function (Filter)

Retrieves a value from a table.

Syntax

LOOKUPVALUE ( <Result_ColumnName>, <Search_ColumnName>, <Search_Value> [, <Search_ColumnName>, <Search_Value> [, … ] ] [, <Alternate_Result>] )
Parameter Attributes Description
Result_ColumnName

The column that contains the desired value.

Search_ColumnName Repeatable

The column that contains search_value.

Search_Value Repeatable

The value that you want to find in search_column.

Alternate_Result Optional

The value that is returned when there is no value or more than one value in the specified column; if omitted, BLANK is returned for no value and an error is returned for more than one value.

Return values

Scalar A single value of any type.

The value of Result_Column at the row where all pairs of Search_Column and Search_Value have a match.

Remarks

If there is no match that satisfies all the search values, a BLANK is returned. In other words, the function will not return a lookup value if only some of the criteria match.

If multiple rows match the search values and in all cases Result_Column values are identical then that value is returned. However, if Result_Column returns different values an error is returned.

The Search_ColumnName can be any column of the expanded table referenced by Result_ColumnName.

Internally LOOKUPVALUE generates this equivalent syntax:

VAR SearchValue = <Search_Value>
RETURN
    CALCULATE (
        SELECTEDVALUE ( <Result_ColumnName>, <Alternate_Result> ),
        FILTER (
            ALLNOBLANKROW ( <Search_ColumnName> ),
            <Search_ColumnName> == SearchValue      -- The == operator distinguishes between blank and 0/empty string
        ),
        ALL ( <table_of_Result_ColumnName> )        -- If Result_ColumnName is t, this is ALL ( t )
    )

Performance considerations

Consider using LOOKUPVALUE when it is not possible to rely on RELATED to leverage an existing relationship in the data model, because RELATED is faster.
The query plan generated by LOOKUPVALUE is usually relatively optimized. However, in certain conditions it could include CallbackDataID requests in storage engine queries, resulting in slower execution, lower performance and missed cache hits. When this happens, alternative approaches should be considered.

Consider the LOOKUPVALUE syntax:

LOOKUPVALUE (
    table[result_column],
    table[search_column_1], <expression_1>,
    table[search_column_2], <expression_2>,
    <alternate_result>
)

You can write LOOKUPVALUE using CALCULATE this way:

CALCULATE (
    SELECTEDVALUE ( table[result_column], <alternate_result> ),
    FILTER (
        ALLNOBLANKROW ( table[search_column_1] ),
        table[search_column_1] == <expression_1>
    ),
    FILTER (
        ALLNOBLANKROW ( table[search_column_2] ),
        table[search_column_2] == <expression_2>
    ),
    REMOVEFILTERS ( ) 
)

When <expression_1> and <expression_2> are constant values, there shouldn’t be any issue. However, it is often the case that these expressions are more dynamic, and this could generate a more expensive query plan that includes CallbackDataID requests to the storage engine. This might also be the case for the LOOKUPVALUE function.
In order to reduce this effort, you can move the expression outside of the filter predicates in CALCULATE; a similar approach in LOOKUPVALUE might not produce a similar level of optimization:

VAR filterValue1 = <expression_1>
VAR filterValue2 = <expression_2>
RETURN CALCULATE (
    DISTINCT ( table[result_column] ),
    table[search_column_1] = filterValue1,
    table[search_column_2] = filterValue2,
    REMOVEFILTERS ( ) 
)

The code above corresponds to the following approach using TREATAS:

CALCULATE (
    DISTINCT ( table[result_column] ),
    TREATAS ( { <expression_1> }, table[search_column_1] ),
    TREATAS ( { <expression_2> }, table[search_column_2] ),
    REMOVEFILTERS ( ) 
)

It is better to store the TREATAS result in a variable for readability reasons, but the following code is also identical to the previous code from a query plan perspective:

VAR filter1 = TREATAS ( { <expression_1> }, table[search_column_1] )
VAR filter2 = TREATAS ( { <expression_2> }, table[search_column_2] )
RETURN CALCULATE (
    DISTINCT ( table[result_column] ),
    filter1,
    filter2,
    REMOVEFILTERS ( ) 
)

For the LOOKUPVALUE use case, it is possible to create a single multi-column filter instead of multiple filters – possibly resulting in a better query plan. A benchmark of different solutions is always a good idea:

VAR filterLookup = 
    TREATAS ( 
        { ( <expression_1>, <expression_2> ) }, 
        table[search_column_1], 
        table[search_column_2] 
    ) 
RETURN CALCULATE (
    DISTINCT ( table[result_column] ),
    filterLookup,
    REMOVEFILTERS ( ) 
)

This latter alternative to LOOKUPVALUE could optimize complex scenarios where the presence of LOOKUPVALUE in an iterator produces poor performance – mainly when the storage engine queries include CallbackDataID calls and are not stored in cache.

Examples

Search the Rate for a given date and currency included in the same table:

LOOKUPVALUE (
    ExchangeRates[Rate],
    ExchangeRates[Date], DATE ( 2018, 4, 15 ),
    ExchangeRates[Currency], "EUR"
)

Search the Rate for a given date and currency code defined in a related table:

LOOKUPVALUE (
    ExchangeRates[AverageRate],
    ExchangeRates[Date], DATE ( 2018, 4, 15 ),
    Currency[Currency Code], "EUR"
)

Related articles

Learn more about LOOKUPVALUE in the following articles:

  • Lookup multiple values in DAX

    This article describes different techniques to retrieve multiple values from a lookup table in DAX, improving code readability and performance. » Read more

  • From SQL to DAX: Joining Tables

    In SQL there are different types of JOIN, available for different purposes. This article shows the equivalent syntaxes supported in DAX and it was updated in May 2018. » Read more

  • USERELATIONSHIP in Calculated Columns

    In a Power Pivot or Tabular model with inactive relationships, one can rely on the USERELATIONSHIP function to apply an inactive relationship to a particular DAX expression. Its usage is simple in a measure, but one might consider alternative syntax in calculated columns, as is explained in this article. » Read more

  • Expanded tables in DAX

    Expanded tables are the core of DAX; understanding how they work is of paramount importance. This article provides a theoretical foundation of what expanded tables are, along with fundamental concepts useful when reading DAX code. » Read more

Last update: Dec 7, 2019   » Contribute   » Show contributors

Contributors: Alberto Ferrari, Marco Russo

MSDN documentation: https://docs.microsoft.com/en-us/dax/lookupvalue-function-dax

DAX Conventions
Context Transition

This function performs a Context Transition if called in a Row Context. Click to read more.

Row Context

This expression is executed in a Row Context. Click to read more.

Iterator

Not recommended

The use of this function is not recommended. See Remarks and Related functions for alternatives.

Not recommended

The use of this parameter is not recommended.

Deprecated

This function is deprecated. Jump to the Alternatives section to see the function to use.

Deprecated

This parameter is deprecated and its use is not recommended.

Contribute

Want to improve the content of LOOKUPVALUE? Did you find any issue?
Please, report it us! All submissions will be evaluated for possible updates of the content.


This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.