{"id":84835,"date":"2019-07-31T14:49:24","date_gmt":"2019-07-31T14:49:24","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=84835"},"modified":"2022-04-24T21:10:21","modified_gmt":"2022-04-24T21:10:21","slug":"cracking-dax-the-earlier-and-rankx-functions","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/cracking-dax-the-earlier-and-rankx-functions\/","title":{"rendered":"Cracking DAX \u2013 the EARLIER and RANKX Functions"},"content":{"rendered":"<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/creating-calculated-columns-using-dax\/\">Creating Calculated Columns Using DAX<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/creating-measures-using-dax\/\">Creating Measures Using DAX<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/using-the-dax-calculate-and-values-functions\/\">Using the DAX Calculate and Values Functions<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/using-the-filter-function-in-dax\/\">Using the FILTER Function in DAX<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/cracking-dax-the-earlier-and-rankx-functions\/\">Cracking DAX\u00a0\u2013 the EARLIER and RANKX Functions<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/using-calendars-and-dates-in-power-bi\/\">Using Calendars and Dates in Power BI<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/bi\/creating-time-intelligence-functions-in-dax\/\"><span>Creating Time-Intelligence Functions in DAX<\/span><\/a><\/li>\n<\/ol>\n\n<p>If you really want to impress people at DAX dinner parties (you know the sort: where people stand around discussing row and filter context over glasses of wine and vol-au-vents?), you\u2019ll need to learn about the <code>EARLIER<\/code> function (and to a lesser extent about the <code>RANKX<\/code> function). This article explains what the <code>EARLIER<\/code> function is and also gives examples of how to use it. Above all, the article provides further insight into how row and filter context work and the difference between the two concepts.<\/p>\n<h2>Loading the Sample Data for this Article<\/h2>\n<p>As for the other articles in this series, the sample dataset consists of four simple tables which you can download from this <a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/Simple-Toys.xlsx\">Excel workbook<\/a>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"211\" height=\"169\" class=\"wp-image-84836\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-71.png\" \/><\/p>\n<p>After importing the data, you should see this model diagram (I\u2019ve used Power BI, but the formulae in this article would work equally well in PowerPivot or SSAS Tabular):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1001\" height=\"223\" class=\"wp-image-84837\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-72.png\" \/><\/p>\n<p>One more thing to do \u2013 add a calculated column to the country table, giving the total sales for each country:<\/p>\n<pre class=\"lang:c# theme:vs2012\">TotalSales = CALCULATE(Sumx(Sales,[Price]*[Quantity]))<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"626\" height=\"204\" class=\"wp-image-84838\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-73.png\" \/><\/p>\n<p>This formula iterates over the <em>Sales<\/em> table, calculating the sales for each row (the quantity of goods sold, multiplied by the price) and summing the figures obtained. The <code>CALCULATE<\/code> function is needed because you must create a filter context for each row so that you\u2019re only considering sales for that country \u2013 without it, you would get this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"329\" height=\"155\" class=\"wp-image-84839\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-74.png\" \/><\/p>\n<h2>Example 1: Ranking the Countries<\/h2>\n<p>The first use case is very simple. The aim is to rank the countries by the total sales for each:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"434\" height=\"159\" class=\"wp-image-84840\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-75.png\" \/><\/p>\n<p>DAX doesn\u2019t have an equivalent of the SQL <code>ORDER BY<\/code> clause, but you can rank data either by using the <code>RANKX<\/code> function (covered later in this article) or by using the <code>EARLIER<\/code> function creatively. Here\u2019s what the function needs to do, using country number 3 \u2013 India \u2013 as an example. Firstly, it creates a row context for this row:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"468\" height=\"179\" class=\"wp-image-84841\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-76.png\" \/><\/p>\n<p>The sales for India are 34.5. What it now must do is to count how many rows there are in the table which have countries whose sales are greater than or equal to India\u2019s sales:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"184\" class=\"wp-image-84842\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-77.png\" \/><\/p>\n<p>This shows the filtered table of countries, including only those whose sales are at least equal to India\u2019s. There are four rows in this filtered table.<\/p>\n<p>If you perform the same calculations for each of the other countries in the table, you\u2019ll get the ranking order for each (that is, the number of countries whose sales match or exceed each country\u2019s). For those who know SQL, this works in the same way as a correlated subquery. This would imply that it might run slowly, however, here\u2019s what Microsoft <a href=\"https:\/\/docs.microsoft.com\/en-us\/dax\/earlier-function-dax\">have to say<\/a> on the subject:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"679\" height=\"368\" class=\"wp-image-84843\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-78.png\" \/><\/p>\n<h3>The problem: One Row Context Hides Another<\/h3>\n<p>Anyone who has driven much in France will have seen this sign at a level-crossing:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"251\" height=\"145\" class=\"wp-image-84844\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-79.png\" \/><\/p>\n<p>What it means is \u201cone train can hide another\u201d &#8230; and so it is with row contexts. The measure will open two row contexts, as the diagram below shows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"829\" height=\"359\" class=\"wp-image-84845\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-80.png\" \/><\/p>\n<p>The problem is that when DAX gets to the inner row context for the <code>FILTER<\/code> function, it will no longer be able to see the outer row context (the country you\u2019re currently considering) as this row context will be hidden unless you use the <code>EARLIER<\/code> function to refer to the first row context created in the formula.<\/p>\n<h3>What Would You Name this Function?<\/h3>\n<p>I\u2019ve explained that the <code>EARLIER<\/code> function refers to the original row context created (nearly always for a calculated column). What would you have called this function? I\u2019d be tempted by one of these names:<\/p>\n<ul>\n<li>OUTERROWCONTEXT<\/li>\n<li>PREVIOUSROWCONTEXT<\/li>\n<li>INITIALROWCONTEXT<\/li>\n<\/ul>\n<p>The multi-dimensional (cubes) version of Analysis Services gets it about right, using <code>CURRENTMEMBER<\/code> and <code>PREVMEMBER<\/code> depending on context. It\u2019s interesting to see that you can\u2019t use these words as names of variables in DAX as they are reserved words:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"335\" height=\"67\" class=\"wp-image-84846\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-81.png\" \/><\/p>\n<p>This is true even though they aren\u2019t official DAX function names!<\/p>\n<p>What I definitely wouldn\u2019t use for the function name is something which implied the passage of time. To me, <code>EARLIER<\/code> means something which occurred chronologically before something else. I think this is one of the reasons it took me so long to understand the <code>EARLIER<\/code> function: it\u2019s just got such an odd name.<\/p>\n<h3>The Final Formula Using EARLIER<\/h3>\n<p>Having got that off my chest, here\u2019s the final formula:<\/p>\n<pre class=\"lang:c# theme:vs2012\">SalesOrder = COUNTROWS(\r\n    Filter(\r\n        Country,\r\n        [TotalSales] &gt;= EARLIER([TotalSales])\r\n   )\r\n)<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"388\" height=\"310\" class=\"wp-image-84847\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-82.png\" \/><\/p>\n<p>Here\u2019s the English (!) translation of this &#8230;<\/p>\n<p>\u201cFor each country in the countries table, create a row context (this happens automatically for any calculated column). For this row\/country being considered, count the number of rows in a separate virtual copy of the table for which the total sales for the country are greater than or equal to the total sales for the country being considered in the original row context\u201d.<\/p>\n<p>What could possibly be confusing about that?<\/p>\n<h3>Other Forms of the EARLIER Function<\/h3>\n<p>Readers will be delighted to know that you don\u2019t have to limit yourself to going back to the previous row context \u2013 you can specify how many previous row contexts to return to by varying the number used in the second argument to the <code>EARLIER<\/code> function:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"595\" height=\"95\" class=\"wp-image-84848\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-83.png\" \/><\/p>\n<p>You can even use the <code>EARLIEST<\/code> function to go to the earliest row context created for a formula. The formula could alternatively have been written like this:<\/p>\n<pre class=\"lang:c# theme:vs2012\">SalesOrder = COUNTROWS(\r\n    FILTER(\r\n        Country,\r\n        [TotalSales] &gt;= EARLIEST([TotalSales])\r\n    )\r\n)<\/pre>\n<p>I find it very hard to believe that anyone would need this function! It could only be useful where you:<\/p>\n<ol>\n<li>Create a row context (typically by creating a calculated column).<\/li>\n<li>Within this, create a filter context.<\/li>\n<li>Within this, create a row context (typically by using an iterator function like <code>FILTER<\/code> or <code>SUMX<\/code>).<\/li>\n<li>Within this, create another filter context.<\/li>\n<li>Within this, create a third-row context.<\/li>\n<\/ol>\n<p>In this complicated case, the <code>EARLIER<\/code> function would refer to the row context created at step 3, but the <code>EARLIEST<\/code> function would refer to the original row context created at step 1.<\/p>\n<h3>Avoiding the EARLIER Function by Using Variables<\/h3>\n<p>One of the surprising things about the <code>EARLIER<\/code> function is that you often don\u2019t need it. For this example, you could store the total sales for each country in a variable, and reference this instead. The calculated column would read instead:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Sales order using variables = \r\n\/\/ create a variable to hold each country's sales\r\nVAR TotalSalesThisCountry = [TotalSales]\r\n\/\/ now count how many countries have sales \r\n\/\/ which match or exceed this\r\nRETURN COUNTROWS(\r\n    FILTER(\r\n        Country,\r\n        [TotalSales] &gt;= TotalSalesThisCountry\r\n    )\r\n)<\/pre>\n<p>There\u2019s not much doubt that this is easier to understand, but it won\u2019t give you the same insight into how row context works!<\/p>\n<h3>Using the EARLIER function in a measure<\/h3>\n<p>The <code>EARLIER<\/code> function refers to the previous row context created in a DAX formula. But what happens if there isn\u2019t a previous row context? This is why you will so rarely see the <code>EARLIER<\/code> function used in a measure. Here\u2019s what you\u2019ll get if you put the formula in a measure:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"469\" height=\"163\" class=\"wp-image-84849\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-84.png\" \/><\/p>\n<p>The yellow error message explains precisely what the problem is: measures use filter context, not row context, so no outer row context is created.<\/p>\n<h2>Example 2: Running Totals<\/h2>\n<p>Another common requirement is calculating the cumulative total sales for countries in alphabetical order:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"563\" height=\"156\" class=\"wp-image-84850\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-85.png\" \/><\/p>\n<p>The above screenshot shows that this calculates the answer separately for each country, but it looks more sensible when you view it in alphabetical order by country name:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"563\" height=\"155\" class=\"wp-image-84851\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-86.png\" \/><\/p>\n<p>The formula for this column calculates for each country the total sales for all countries coming on or before it in alphabetical order:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Running total = SUMX(\r\n    FILTER(\r\n        Country,\r\n        Country[CountryName] &lt;= EARLIER(Country[CountryName])\r\n    ),\r\n    [TotalSales]\r\n)<\/pre>\n<p>Again, you could have alternatively used a variable to hold each country\u2019s name:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Running total using variables = \r\n\/\/ store this row's country name\r\nVAR ThisCountry = Country[CountryName]\r\n\/\/ return the sum of sales for all countries up to or\r\n\/\/ including this one\r\nRETURN SUMX(\r\n    FILTER(\r\n        Country,\r\n        Country[CountryName] &lt;= ThisCountry\r\n    ),\r\n    [TotalSales]\r\n)<\/pre>\n<h2>Example 3: Group Totals<\/h2>\n<p>Here\u2019s another use of the <code>EARLIER<\/code> function \u2013 to create group totals. To follow this example, first, add a calculated column in the Sales table to show the name of each product bought (you don\u2019t have to do this, but it will make the example clearer if you use the product name rather than the product id to match sales rows to the product bought):<\/p>\n<p>Product = RELATED(&#8216;Product'[ProductName])<\/p>\n<p>Here\u2019s the column this should give:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"714\" height=\"163\" class=\"wp-image-84852\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-87.png\" \/><\/p>\n<p>You can now create a second calculated column:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Average product sales = AVERAGEX(\r\n    \r\n    \/\/ average over the table of sales records for the \r\n    \/\/ same product as this one ...\r\n    FILTER(Sales,[Product] = EARLIER(Sales[Product])),\r\n    \r\n    \/\/ ... the total value of the sale\r\n    [Price]*[Quantity]\r\n)<\/pre>\n<p>Here\u2019s what this will give for the first few roles in the <em>SALES<\/em> table. You may want to format the column as shown by modifying the decimal places on the Modeling tab.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"454\" height=\"196\" class=\"wp-image-84853\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-88.png\" \/><\/p>\n<p>The group average for Olly Owl products is 5.40. For this example, for each sales row, the calculated column formula is averaging the value of all sales where the product matches the one for the current row.<\/p>\n<h2>Example 4: Creating Bands<\/h2>\n<p>So far, you have seen three relatively simple uses of the <code>EARLIER<\/code> function \u2013 for the fourth example, I\u2019ll demonstrate a more sophisticated one. Begin by adding a calculated column to the <em>Sales<\/em> table, giving the value of each transaction (which should equal the number of items bought, multiplied by the price paid for each):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"706\" height=\"174\" class=\"wp-image-84854\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-89.png\" \/><\/p>\n<p>It would be good to categorise the sales into different bands, to allow reporting on different customer bands separately. For example:<\/p>\n<ul>\n<li>Any purchase with value up to \u00a310 is assigned to \u201cLow value\u201d<\/li>\n<li>Any purchase with value between \u00a310 and \u00a315 is assigned to \u201cMedium value\u201d<\/li>\n<li>Any purchase with value between \u00a315 and \u00a320 is \u201cHigh value\u201d<\/li>\n<li>Any purchase of more than \u00a320 is \u201cPremium customer\u201d<\/li>\n<\/ul>\n<p>One way to do this would be to create a calculated column using a <code>SWITCH<\/code> function like this:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Customer type = SWITCH(\r\n    \/\/ try to find an expression which is true\r\n    TRUE(),\r\n    \/\/ first test - low value\r\n    [SalesValue] &lt;= 10, \"Low value\",\r\n    \/\/ second test - medium value\r\n    [SalesValue] &lt;= 15, \"Medium value\",\r\n    \/\/ third test - high value\r\n    [SalesValue] &lt;= 20, \"High value\",\r\n    \/\/ otherwise, they are a premium customer\r\n    \"Premium customer\"\r\n)<\/pre>\n<p>This would allow reporting on the different customer types (for example, using this table):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"281\" height=\"167\" class=\"wp-image-84855\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-90.png\" \/><\/p>\n<p>However, this hard-codes the thresholds into the calculated column. What would be much more helpful is if you could import the thresholds from another table, to make a dynamic system (similar to a lookup table in Excel, for those who know these things).<\/p>\n<h3>Creating the Table of Bands (Thresholds)<\/h3>\n<p>To create a suitable table, in Power BI Desktop choose to enter data in a new table (you could also type it into Excel and load the resulting workbook):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"547\" height=\"227\" class=\"wp-image-84856\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-91.png\" \/><\/p>\n<p>Type in the thresholds that you want to create, and give your table the name <em>Categories<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"658\" height=\"307\" class=\"wp-image-84857\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-92.png\" \/><\/p>\n<p>Note that in your model\u2019s relationship diagram, you will now have a data island (a table not linked to any other). This is intentional:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"498\" height=\"299\" class=\"wp-image-84858\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-93.png\" \/><\/p>\n<h3>Creating a Formula Assigning Each Sale Row to a Category<\/h3>\n<p>You can now go to the <em>Sales<\/em> table and create a calculated column assigning each row to the correct category:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"484\" height=\"323\" class=\"wp-image-84859\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-94.png\" \/><\/p>\n<p>For each row, what you want to do is find the set of rows in the <em>Categories<\/em> table where two conditions are true:<\/p>\n<p>The lower band for the category is less than the sales for the row; and<\/p>\n<p>The upper band for the category is greater than or equal to the sales for the row.<\/p>\n<p>It should be reasonably obvious that this set of rows will always exist, and will always have exactly one row in it. Because of this, you can use the <code>VALUES<\/code> function to return the value of this single row, giving:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Category = CALCULATE(\r\n    \/\/ return the only category which satisfies both of\r\n    \/\/ the conditions given\r\n    VALUES(Categories[CategoryName]),\r\n    \/\/ this sales must be more than the lower band ...\r\n    Categories[Low] &lt; EARLIER(Sales[SalesValue]),\r\n    \/\/ ... and less than or equal to the higher band\r\n    Categories[High] &gt;= EARLIER(Sales[SalesValue])\r\n)<\/pre>\n<p>You might at this point wonder why you need the <code>EARLIER<\/code> function when you haven\u2019t created a second row context. The answer is that the <code>CALCULATE<\/code> function creates a filter context, so you need to tell your formula to refer back to the original row context you first created in the formula.<\/p>\n<h3>Sorting the Categories<\/h3>\n<p>One problem remains: the categories aren\u2019t in the right order (I\u2019m well aware that one way to solve this would be to click on the column heading to change the sort order, but I\u2019m after something more dynamic!):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"224\" height=\"164\" class=\"wp-image-84860\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-95.png\" \/><\/p>\n<p>To get around this, it may at first appear that all you need to do is to designate a sort column:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"561\" height=\"333\" class=\"wp-image-84861\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-96.png\" \/><\/p>\n<p>You could do this by selecting the <em>CategoryName<\/em> column in the <em>Categories<\/em> table as above, and on the <em>Modeling<\/em> tab, setting this to be sorted by the <em>SortOrder<\/em> column. This looks promising \u2013 but doesn\u2019t work (it actually creates an error if you\u2019ve typed in the data for the categories table, which you\u2019ll need to refresh your model to clear). The reason this doesn\u2019t work is that the alternative sort column needs to be in the same table as the <em>Category<\/em> column.<\/p>\n<p>A solution is to create another calculated column in the <em>Sales<\/em> table:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Category SortOrder = CALCULATE(\r\n    \/\/ return the sales order for the two conditons\r\n    VALUES(Categories[SortOrder]),\r\n    \/\/ this sales must be more than the lower band ...\r\n    Categories[Low] &lt; EARLIER(Sales[SalesValue]),\r\n    \/\/ ... and less than or equal to the higher band\r\n    Categories[High] &gt;= EARLIER(Sales[SalesValue])\r\n)<\/pre>\n<p>You now have two columns for each sale row \u2013 one giving the category, and one giving its corresponding sort order number. You can select the category column and choose to sort it using the sort order column instead (choosing the option shown below on the Power BI Modeling tab):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"995\" height=\"434\" class=\"wp-image-84862\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-97.png\" \/><\/p>\n<p>Bingo! The categories appear in the required order:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"225\" height=\"166\" class=\"wp-image-84863\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-98.png\" \/><\/p>\n<p>If after reading this far you\u2019re still a bit fuzzy on the difference between row and filter context, practice makes perfect! I\u2019ve found reading and re-reading articles like this helps, as each time you get closer to perfecting your understanding (for those in the UK, you could also consider booking onto one of Wise Owl\u2019s <a href=\"https:\/\/www.wiseowl.co.uk\/power-bi\/\">Power BI courses<\/a>).<\/p>\n<h2>Using the RANKX Function to Rank Data<\/h2>\n<p>Out of all the DAX functions, the one which trips me up most often is <code>RANKX<\/code> \u2013 which is odd, because what it does isn\u2019t particularly complicated. Here\u2019s the syntax of the function:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"587\" height=\"92\" class=\"wp-image-84864\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-99.png\" \/><\/p>\n<p>So the function ranks a table by a given expression. Here is what the arguments mean:<\/p>\n<ul>\n<li>The Table argument gives the table you\u2019re ranking over.<\/li>\n<li>The Expression argument gives the expression you\u2019re ranking by.<\/li>\n<li>The Order column allows you to switch between ascending and descending order.<\/li>\n<li>The Ties column lets you specify how you\u2019ll deal with ties.<\/li>\n<\/ul>\n<p>You\u2019ll notice I\u2019ve missed out the Value argument. This is for the good reason that it is a) bizarre and b) not useful. It allows you to rank by one expression, substituting in the value for another for each row in a table. If this sounds like a strange thing to want to do, then I\u2019d agree with you.<\/p>\n<p>To see how the <code>RANKX<\/code> function works, return to the <em>Country<\/em> table in which you created a calculated column at the beginning of this article:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"575\" height=\"205\" class=\"wp-image-84865\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-100.png\" \/><\/p>\n<p>Suppose you now want to order the countries by sales (clearly one way to do this would just be to click on the drop arrow next to the column and choose <em>Sort<\/em> ascending!). Here\u2019s a calculated column which would do this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"477\" height=\"194\" class=\"wp-image-84866\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-101.png\" \/><\/p>\n<p>What you should notice is the unusual default ranking order: unlike in SQL (and many other languages), the default order is descending, not ascending. To reverse this, you could specify ASC as a value for the fourth Order argument:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Sales order = RANKX(\r\n    \r\n    \/\/ rank the rows in the country table ...\r\n    Country,\r\n    \r\n    \/\/... by the total sales column ...\r\n    [TotalSales],\r\n    \r\n    \/\/ omitting the third argument\r\n    ,\r\n    \r\n    \/\/ and ranking in ascending order\r\n    ASC\r\n)<\/pre>\n<p>So far, so straightforward!<\/p>\n<h3>Using RANKX in a Measure<\/h3>\n<p>Where things get more complicated is when you use <code>RANKX<\/code> in a measure, rather than in a calculated column. Suppose that you have created a table visual like this in a Power BI report:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"225\" height=\"178\" class=\"wp-image-84867\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-102.png\" \/><\/p>\n<p>Suppose further that you want to create and display this measure to show the ranking order:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Sort order measure = RANKX(\r\n    \/\/ order the countries by sales\r\n    Country,\r\n    SUMX(\r\n        Sales,\r\n        [Price]*[Quantity]\r\n    )\r\n)<\/pre>\n<p>However, when you add this measure to your table, you get this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"252\" height=\"196\" class=\"wp-image-84868\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-103.png\" \/><\/p>\n<p>The reason for this is that Power BI evaluates the measure within the filter context of the table. So for the first row (Brazil), for example, here\u2019s what Power BI does:<\/p>\n<ul>\n<li>Applies the filter context to the Country table to pick out just sales for Brazil<\/li>\n<li>Calculates the total sales for this country (14, as it happens)<\/li>\n<li>Returns the position of this figure within the table of all of the figures for the current filter context (so the returned number 1 means that 14 is the first item in a table containing the single value 14)<\/li>\n<\/ul>\n<p>To get around this, you first need to remove filter context before calculating the ranking order, so that you rank the total sales for each country against all of the other country\u2019s total sales:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"306\" height=\"212\" class=\"wp-image-84869\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-104.png\" \/><\/p>\n<p>However, even this doesn\u2019t solve the problem. The increasingly subtle problem is that the <code>RANKX<\/code> function \u2013 being an iterator function &#8211; creates a row context for each country, but then evaluates the sales to be the same for each country because it doesn\u2019t create a filter context within this row context. To get around this, you need to add a <code>CALCULATE<\/code> function to perform context transition from row to filter context within the row context within the measure:<\/p>\n<pre class=\"lang:c# theme:vs2012\">Sort order measure = RANKX(\r\n    \/\/ order across ALL the countries ...\r\n    ALL(Country),\r\n    \/\/ by the total sales\r\n    CALCULATE(\r\n        SUMX(\r\n            Sales,\r\n            [Price]*[Quantity]\r\n            )\r\n        )\r\n    )<\/pre>\n<p>This \u2013 finally \u2013 produces the correct ranking (note that there is a blank country at the top because some sales don\u2019t have a matching country parent \u2013 although this isn\u2019t important for this example):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"254\" height=\"172\" class=\"wp-image-84870\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-105.png\" \/><\/p>\n<p>It\u2019s worth making two points about this. Firstly, this isn\u2019t a formula you\u2019ll often need to use; and secondly, this is about as complicated a DAX formula as you will ever see!<\/p>\n<h3>Dealing with Ties<\/h3>\n<p>If your ranking produces ties, the default is to SKIP numbers, but you can also use the <code>DENSE<\/code> keyword to override this. To see what each keyword means, add this calculated column to the <em>Sales<\/em> table:<\/p>\n<pre class=\"lang:c# theme:vs2012\">QtyRanking = RANKX(Sales,[Quantity])<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"669\" height=\"232\" class=\"wp-image-84871\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-106.png\" \/><\/p>\n<p>What it will do is to order the sales rows by the quantity sold. Here\u2019s what you\u2019ll get if you use Skip, or omit the Ties keyword altogether:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"607\" height=\"374\" class=\"wp-image-84872\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-107.png\" \/><\/p>\n<p>Here, by contrast, is what you\u2019ll get if you use <code>Dense<\/code> (I\u2019m not quite sure why you\u2019d ever want to do this):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"609\" height=\"385\" class=\"wp-image-84873\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/07\/word-image-108.png\" \/><\/p>\n<p>In the second screenshot, there are no gaps in the numbers.<\/p>\n<h2>Conclusion<\/h2>\n<p>You can apply the <code>EARLIER<\/code> function in various contexts to solve modelling problems in DAX, and it should be an essential part of every DAX programmer\u2019s arsenal. The <code>RANKX<\/code> function, by contrast, solves a very specific problem \u2013 ranking data. What both functions have in common is that they are impossible to understand without a deep understanding of row and filter context, so if you\u2019ve got this far, you can consider yourself a DAX guru!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So far in this series, Andy Brown of Wise Owl Training has shed light on functions like CALCULATE, VALUES and FILTER, but it\u2019s only when you understand the idiosyncrasies of the EARLIER function that you can claim to have genuinely cracked DAX.  This article gives four examples of the use of this peculiar function, in the process explaining why it has such a misleading name.  Using the EARLIER function properly all boils down (as is so often the case with DAX) to understanding row and filter context.  The article also shows how to use the RANKX function to sort data into your required order.&hellip;<\/p>\n","protected":false},"author":9783,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143528],"tags":[],"coauthors":[6782],"class_list":["post-84835","post","type-post","status-publish","format-standard","hentry","category-bi-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84835","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/9783"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=84835"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84835\/revisions"}],"predecessor-version":[{"id":84876,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/84835\/revisions\/84876"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=84835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=84835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=84835"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=84835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}