Roughly a year ago, I implemented currency conversion logic in multidimensional cubes using measure expressions (as well as using the Leaves function in MDX). I documented the implementation in a few blog posts:
I have now also implemented currency conversion in a tabular cube using DAX. This blog post covers the implementation in DAX as well as some of the performance issues I encountered and how I resolved them.
Data Model Overview
The tables required for currency conversion in the tabular cube are the same as those in multidimensional.
The diagram below shows the relationships between these tables (ignoring target currency in this tabular cube implementation).
The following is a screen shot of the tabular cube table relationships:
The tables relevant to the currency conversion are Sales, GL Date, Currency Conversion Rate, Currency Code (Source Currency), Currency Conversion Rate Type. The tables grayed out are hidden in end-user client tools.
Currency Conversion Implementation Using DAX
The following measure was added to the Sales table of the tabular model to perform the currency conversion:
Sales Amount USD:=SUMX(CROSSJOIN(‘Currency Code’,'Currency Conversion Rate Type’,'GL Date’),CALCULATE(VALUES(‘Currency Conversion Rate’[CurrencyRateOv]) * SUM(‘Sales’[SalesAmountLocal])))
I learned from Marco Russo’s PASS Summit presentation (Inside DAX Query Plans) that this formula involves a single scan of a temporary table (single SUMX). The SUMX over a CROSSJOIN is resolved as a single VertiPaq scan using CallbackDataID to compute the multiplication.
When I connected to the cube in Excel and sliced the measure by an attribute with a small number of distinct values, the performance was very good; but, when I selected an attribute with a larger number of distinct values (greater than 5000), the measure never returned a value.
I improved the performance by creating a Calculated Column in the Sales table to store the exchange rate for each transaction (row). It’s important to note that Calculated Column values are calculated during processing and not ‘on-the-fly’ like measures.
The DAX formula for the ‘ConversionRate’ calculated column is shown below:
=LOOKUPVALUE(‘Currency Conversion Rate’[CurrencyRateOv], ‘Currency Conversion Rate’[DateKey], Sales[GlDateKey], ‘Currency Conversion Rate’[FromCurrencyKey], Sales[CurrencyCodeKey], ‘Currency Conversion Rate’[CurrencyRateTypeKey], 2)
LOOKUPVALUE performs a lookup of the conversion rate for the transaction based on the GL Date (GlDateKey), Source Currency (CurrencyCodeKey), and the Currency Rate Type (CurrencyRateTypeKey).
The measure to perform the actual currency conversion using the ConversionRate calculated column is:
Sales Amount USD:=SUMX(‘Sales’,'Sales’[SalesAmountLocal] * ‘Sales’[ConversionRate])
When I connected to the cube in Excel and sliced the measure by an attribute with 7000+ distinct values, the performance was very good.
Although I was disappointed that I was unable to implement this calculation as a measure without the need for a calculated column; I am happy that this alternate implementation delivers excellent performance for the end-users.