{"id":93175,"date":"2022-01-21T15:29:58","date_gmt":"2022-01-21T15:29:58","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=93175"},"modified":"2022-02-15T15:14:05","modified_gmt":"2022-02-15T15:14:05","slug":"dax-table-functions-for-paginated-reports-part-1","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/dax-table-functions-for-paginated-reports-part-1\/","title":{"rendered":"DAX table functions for paginated reports: Part 1"},"content":{"rendered":"<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/introduction-to-dax-for-paginated-reports\/\">Introduction to DAX for paginated reports<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/how-to-filter-dax-for-paginated-reports\/\">How to filter DAX for paginated reports<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/dax-table-functions-for-paginated-reports-part-1\/\"> \u00a0DAX table functions for paginated reports: Part 1<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/dax-table-functions-for-paginated-reports-part-2\/\">DAX table functions for paginated reports: Part 2<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/tips-and-tricks-with-dax-table-functions-in-paginated-reports\/\">Tips and tricks with DAX table functions in paginated reports<\/a><\/li>\n<\/ol>\n\n<p>In the previous articles, you learned \u2013 or revised \u2013 the basics of using DAX as the query language to populate paginated reports with data from Power BI datasets. However, as befitted an introduction, the focus was essentially on getting up and running. Specifically, the only DAX table function you looked at was <code>SUMMARIZECOLUMNS()<\/code>.<\/p>\n<p>Despite its undeniable usefulness, this function is far from the only DAX function that you can use to query Power BI Datasets when creating Paginated Reports. Moreover, it has limitations when you wish to deliver complete lists of results as it is an aggregation function. This means, for instance, that you will never find duplicate records in the tabular output from <code>SUMMARIZECOLUMNS()<\/code> as, by default, it is grouping data. Alternatively, if you wish to use <code>SUMMARIZECOLUMNS()<\/code> to output data at its most granular level, you will need to include a unique field (or a combination of fields that guarantee uniqueness) \u2013 even if these are not used in the report output.<\/p>\n<p>It follows that, to extract data in ways that allow effective report creation, it is essential to learn to use a whole range of DAX table functions. This article covers:<\/p>\n<ul>\n<li>SELECTCOLUMNS<\/li>\n<li>SUMMARIZE<\/li>\n<li>GROUPBY<\/li>\n<li>ADDCOLUMNS<\/li>\n<\/ul>\n<p>As well as these core table functions, it also helps to understand the <code>DEFINE<\/code>, <code>EVALUATE<\/code> and <code>MEASURE<\/code> functions as well as the use of variables in DAX queries when returning data to paginated reports.<\/p>\n<p>The purpose of this article (and the following one) is to introduce you, as a Paginated Report developer, to the collection of DAX functions that range from the marginally useful to the absolutely essential when returning data to paginated reports from Power BI. Once again, I am presuming that you are not a battle-hardened DAX developer. While you may have some experience developing DAX measures in Power BI Desktop, you now need to extend your DAX knowledge to extract data in a tabular format from a Power BI dataset so that you can use the output in paginated reports.<\/p>\n<p>At this point, it is entirely reasonable to wonder why you should ever need anything more than <code>SUMMARIZECOLUMNS()<\/code> to return data from a Power BI dataset. After all, it is the standard function used by the Power BI Report Builder and works perfectly well in many cases.<\/p>\n<p>While being suitable for many output datasets, <code>SUMMARIZECOLUMNS()<\/code> is essentially an aggregation function. In many cases, you may want to return a filtered list (pretty much like a simple SQL <code>SELECT<\/code> query) rather than a <code>GROUP BY SQL<\/code> query which is what <code>SUMMARIZECOLUMNS()<\/code> delivers.<\/p>\n<p>There are also some data output challenges that require other DAX functions \u2013 or indeed, combinations of DAX functions &#8211; to return the required data. A grasp of the wider suite of DAX table functions will leave you better armed to answer a range of potential challenges when creating paginated reports.<\/p>\n<p>Many DAX table functions have quirks and limitations that can trip up the unwary user in certain circumstances. However, the aim of these two articles is not to provide an exhaustive discussion of every available function and the hidden depths and rarer aspects of each one. Rather, the intention is to provide developers with an initial overview of the core tools that you will inevitably need to extract data for paginated reports from a Power BI dataset.<\/p>\n<h2>EVALUATE<\/h2>\n<p>If your requirements are as simple as returning the complete contents of a table from the source Power BI dataset to a paginated report, then you can simply use the <code>EVALUATE<\/code> keyword, like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nFactSales<\/pre>\n<p>The output from the sample dataset is as shown below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"878\" height=\"195\" class=\"wp-image-93191\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-24.png\" \/><\/p>\n<p>Indeed, this principle can be extended to reduce the output from a single table by enclosing the table to evaluate in a <code>FILTER()<\/code> function, like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nFILTER(\r\nFactSales\r\n,FactSales[CostPrice] &gt; 50000\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"200\" class=\"wp-image-93192\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-25.png\" \/><\/p>\n<p>Indeed, you can even filter using values from other tables if you include the <code>RELATED()<\/code> function inside the <code>FILTER()<\/code> \u2013 much as you would use it when creating calculated columns in a table that need to refer to other tables\u2019 data. You can see this in the following piece of DAX:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nFILTER(\r\nFactSales\r\n,RELATED(DimGeography[CountryName]) = \"FRANCE\"\r\n)<\/pre>\n<p>Sample output is as shown in the following image (where the Geography_SKs only relate to France):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"144\" class=\"wp-image-93193\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-26.png\" \/><\/p>\n<p>This method means that you can use the full breadth of the data model to filter data even when resorting to the simplest possible DAX.<\/p>\n<p><strong>Note: <\/strong>You need to be aware at this point that you cannot display the filtered field using a simple <code>EVALUATE <\/code>function. However, you can see the field that you are filtering on if you use <code>SELECTCOLUMNS()<\/code> \u2013as is explained in a following section.<\/p>\n<p>When using a <code>FILTER()<\/code> function inside <code>EVALUATE<\/code> the filtering rules are similar to those described in the previous articles in this series. However, you need to be aware that:<\/p>\n<ul>\n<li>Unlike when filtering inside <code>SUMMARIZECOLUMNS()<\/code>, the table that you are evaluating is, by definition, the filtered object (so there is no need to use VALUES(&lt;tablename&gt;) approach that you used when filtering inside a <code>SUMMARIZECOLUMNS()<\/code> function).<\/li>\n<li>You can only filter on a single column per <code>FILTER()<\/code> keyword. Consequently multiple filter elements require nested filters \u2013 like in this example, where I have added an OR condition for good measure):<\/li>\n<\/ul>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nFILTER(\r\nFILTER(\r\nFactSales\r\n,RELATED(DimGeography[CountryName]) = \"FRANCE\")\r\n,RELATED(DimVehicle[Make]) = \"Rolls Royce\"\r\n         || RELATED(DimVehicle[Make]) = \"Jaguar\")<\/pre>\n<p>The output (shortened again) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"162\" class=\"wp-image-93194\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-27.png\" \/><\/p>\n<h2>SELECTCOLUMNS() &#8211; Used to Return Data from a Single Table<\/h2>\n<p>The next DAX table function that delivers non-aggregated output is <code>SELECTCOLUMNS()<\/code>. This function can be very useful when producing fairly simple lists of fields. After all, this is what paginated reports good at delivering, and may be one of the fundamental reasons that you are using paginated reports rather than Power BI to produce a specific report.<\/p>\n<p>You can use <code>SELECTCOLUMNS()<\/code> to:<\/p>\n<ul>\n<li>Build a custom subset of required columns from a table without any aggregation applied.<\/li>\n<li>Change column names.<\/li>\n<li>Apply columns of calculations.<\/li>\n<\/ul>\n<p>As an example, take a look at the following short piece of DAX, which shows the key attributes of the <code>SELECTCOLUMNS()<\/code> function:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSELECTCOLUMNS\r\n(\r\nDimVehicle\r\n,\"Make\", [Make]\r\n,\"Model\", [Model]\r\n,\"Variant\", [ModelVariant]\r\n)<\/pre>\n<p>A subset of the output is as shown in the following image (note that there are many records without a model variant, so you will need to scroll to the bottom of the dataset to see the variant detail):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"257\" height=\"197\" class=\"wp-image-93195\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-28.png\" \/><\/p>\n<p>The key points to note are that:<\/p>\n<ul>\n<li>The first element of <code>SELECTCOLUMNS()<\/code> is the table that you want to return data from.<\/li>\n<li>All the remaining elements are output name\/source column pairs.<\/li>\n<li>The column name to use in the output <strong>must<\/strong> be supplied, must be in double-quotes, and can contain spaces.<\/li>\n<li>You do not have to use fully-qualified field names (table &amp; field) for each output field. The field names themselves must be enclosed in square brackets or double-quotes.<\/li>\n<li><code>SELECTCOLUMNS()<\/code> performs no aggregation on the source table.<\/li>\n<\/ul>\n<p>If, for whatever reason, you want to return distinct values using <code>SELECTCOLUMNS()<\/code>, then you can wrap <code>SELECTCOLUMNS()<\/code> inside a <code>DISTINCT()<\/code> function, like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nDISTINCT\r\n(\r\n  SELECTCOLUMNS\r\n  (\r\n  DimVehicle\r\n  ,\"Make\", [Make]\r\n  ,\"Model\", [Model]\r\n  ,\"Variant\", [ModelVariant]\r\n  )\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"247\" height=\"202\" class=\"wp-image-93196\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-29.png\" \/><\/p>\n<h2>SELECTCOLUMNS() &#8211; Used to Return Data from Multiple Tables<\/h2>\n<p>Another possible step when outputting lists is to use <code>SELECTCOLUMNS()<\/code> to return data from multiple tables in the data model. In this respect, <code>SELECTCOLUMNS()<\/code> is a bit like adding a calculated column in DAX in Power BI in that you have to tell DAX to traverse the dataset if you want to mix and match columns from multiple tables. You can see this in the following DAX snippet:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSELECTCOLUMNS\r\n(\r\nFactSales\r\n,\"Make and Model\", RELATED(DimVehicle[Make]) &amp; \" - \" &amp;\r\n                   RELATED(DimVehicle[Model])\r\n,\"Town\", RELATED(DimGeography[Town])\r\n,\"Gross Margin\", FactSales[SalePrice] - FactSales[CostPrice]\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"295\" height=\"184\" class=\"wp-image-93197\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-30.png\" \/><\/p>\n<p>The points of note at this juncture are:<\/p>\n<ul>\n<li>Use <code>RELATED() to<\/code> traverse the dataset<\/li>\n<li>As <code>RELATED()<\/code> is used, this can mean that only relatively simple data models can be used. Remember, <code>RELATED()<\/code> follows an <strong>existing many-to-one relationship<\/strong> to fetch the value from the specified column in the related table. In other words, if you have a classic star or snowflake schema, then things could work quite easily. The trick may well be to define the fact table as the \u201ccore\u201d table as the <code>SELECTCOLUMNS()<\/code> first parameter, and any dimensional attributes will use <code>RELATED()<\/code>.<\/li>\n<li>It is possible to use field names (without qualifying the field name with the table name) only for the fields from the \u201ccore\u201d table that is specified as the first element in the <code>SELECTCOLUMNS()<\/code> function.<\/li>\n<li>All fields wrapped in the <code>RELATED()<\/code> function must be fully qualified (table and field) field names.<\/li>\n<li><code>SELECTCOLUMNS() <\/code>can also create \u201ccalculated columns\u201d in the output as evidenced by the new \u201cGross Margin\u201d calculation in the DAX above.<\/li>\n<\/ul>\n<p>I find it useful to think of <code>SELECTCOLUMNS()<\/code> as something similar to what you can achieve in Power BI Desktop to extend a tabular data subset with merged and calculated columns. It is all about composing and delivering an output dataset from one or more tables in the data model. Also, if you have been using <code>SUMMARIZECOLUMNS()<\/code> to return data until now, then it is also worth noting that you can add the fields (fields from the core table, fields from related tables and calculations) in any order.<\/p>\n<h2>Filtering Output with SELECTCOLUMNS()<\/h2>\n<p>You can easily filter the output from a <code>SELECTCOLUMNS() <\/code>function. The classic approach is to filter the table that is referred to as the first parameter of <code>SELECTCOLUMNS()<\/code> \u2013 like this<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSELECTCOLUMNS\r\n(\r\nFILTER(FactSales, FactSales[CostPrice] &gt; 50000)\r\n,\"Make\", RELATED(DimVehicle[Make])\r\n,\"Model\", RELATED(DimVehicle[Model])\r\n,\"Town\", RELATED(DimGeography[Town])\r\n,\"Selling Price\", FactSales[SalePrice]\r\n,FactSales[CostPrice]\r\n)<\/pre>\n<p>The output will look something like the shortened snapshot that you can see below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"341\" height=\"251\" class=\"wp-image-93198\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-31.png\" \/><\/p>\n<p>You can, of course, nest logical <code>AND<\/code> filters by nesting <code>FILTER()<\/code> functions \u2013 like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSELECTCOLUMNS\r\n(\r\n  FILTER\r\n  (\r\n    FILTER\r\n    (\r\n           FactSales, FactSales[CostPrice] &gt; 50000\r\n    )\r\n   ,RELATED(DimVehicle[Make]) = \"Bentley\"\r\n  )\r\n,\"Make\", RELATED(DimVehicle[Make])\r\n,\"Model\", RELATED(DimVehicle[Model])\r\n,\"Town\", RELATED(DimGeography[Town])\r\n,\"Selling Price\", FactSales[SalePrice]\r\n, FactSales[CostPrice]\r\n)<\/pre>\n<p>A subset of the output is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"392\" height=\"250\" class=\"wp-image-93199\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-32.png\" \/><\/p>\n<p>Note that any filters that concern columns not present in the core table must be referred to using <code>RELATED()<\/code>. Also, you do not, of course, have to display the fields that you are filtering on. I have done this to make the point that the filters are working as expected.<\/p>\n<p>An alternative approach to filtering the data can be to filter the whole output \u2013 that is, to wrap the entire <code>SELECTCOLUMNS()<\/code> function inside a <code>FILTER()<\/code> function. If you do this, however, the filter can only be applied to a column specified as part of the <code>SELECTCOLUMNS()<\/code> function. Consequently, this approach can return columns that are not required for the report in the output. An example of this technique is given below:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nFILTER(\r\n       SELECTCOLUMNS\r\n       (\r\n        DimVehicle\r\n        ,\"Make\", DimVehicle[Make]\r\n        ,\"Model\", DimVehicle[Model]\r\n        ,\"Variant\", [ModelVariant]\r\n       )\r\n, ISBLANK([Variant])\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"175\" height=\"259\" class=\"wp-image-93200\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-33.png\" \/><\/p>\n<p>There are a few complexities that it is worth being aware of:<\/p>\n<ul>\n<li>You need a clear understanding the data model in order to use the <code>RELATED()<\/code> function.<\/li>\n<li>Measures can be used with <code>SELECTCOLUMNS()<\/code>.<\/li>\n<li>There are many subtleties and variations on the theme of filtering data. Fortunately, filtering datasets returned to paginated reports using <code>SELECTCOLUMNS()<\/code> generally only requires the techniques that you saw in the previous article.<\/li>\n<\/ul>\n<p><code>SELECTCOLUMNS()<\/code> is extremely useful when you want to return simplified list output with a reasonable level of data filtering. The ability to rename columns is also an advantage in certain cases.<\/p>\n<h2>ADDCOLUMNS()<\/h2>\n<p>Another very useful DAX function that you can use to query a Power BI dataset is <code>ADDCOLUMNS()<\/code>. This is:<\/p>\n<ul>\n<li>Similar to <code>SELECTCOLUMNS()<\/code> \u2013 but it <strong>extends<\/strong> a data table rather than starting from scratch with a blank output to which you add further selected columns.<\/li>\n<li>Another \u201cCalculated column\u201d approach to extending an existing non-aggregated data table for output to paginated reports.<\/li>\n<\/ul>\n<p>The following DAX sample illustrates how <code>ADDCOLUMNS()<\/code> can be used:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nADDCOLUMNS\r\n(\r\nFactSales\r\n,\"Gross Profit\", FactSales[SalePrice] - FactSales[CostPrice]\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"899\" height=\"198\" class=\"wp-image-93201\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-34.png\" \/><\/p>\n<p>As you can see from the output, this approach returns all the source table elements, plus any others that have been added. If you need the entire table, then this is faster to code. Of course, sending many columns that will never be used in a table across the ether will consume not only bandwidth but also the processing capacity of the Power BI Service, needlessly \u2013 and possibly expensively. However, if your data model has tables that lend themselves to being used in their entirety, then <code>ADDCOLUMNS()<\/code> can be a very useful addition to your paginated report toolkit.<\/p>\n<p>You can, as was the case with <code>SELECTCOLUMNS()<\/code>, extend the output with fields from other tables using the <code>RELATED()<\/code> function.<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nADDCOLUMNS\r\n(\r\nFactSales\r\n,\"Town\", RELATED(DimGeography[Town])\r\n,\"Gross Profit\", FactSales[SalePrice] - FactSales[CostPrice]\r\n)<\/pre>\n<p>The output (shortened) is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"967\" height=\"200\" class=\"wp-image-93202\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-35.png\" \/><\/p>\n<p>As was the case with <code>SELECTCOLUMNS()<\/code>, you can add the fields (fields from the core table, fields from related tables and calculations) in any order. Once again, this presumes a coherent and well thought out data model.<\/p>\n<p>Now that you have seen the non-aggregation DAX table functions, it is time to take a look at two other aggregation functions \u2013 <code>SUMMARIZE()<\/code> and <code>GROUPBY()<\/code>.<\/p>\n<h2>SUMMARIZE<\/h2>\n<p><code>SUMMARIZE()<\/code> is considered by many DAX aficionados as having lost its relevance since <code>SUMMARIZECOLUMNS()<\/code> was introduced. It is, in many respects, an older, quirky and more verbose version of what is now the standard approach to returning aggregated datasets.<\/p>\n<p>This does not preclude the use of this function. However, as you can see from the code sample below, you have to specify a source table when using <code>SUMMARIZE()<\/code>:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSUMMARIZE(\r\nFactSales\r\n,DimCLient[ClientName]\r\n,DimVehicle[Make]\r\n,\"Cost\", SUM(FactSales[CostPrice])\r\n,\"Sales\", SUM(FactSales[SalePrice])\r\n)\r\nORDER BY DimCLient[ClientName], DimVehicle[Make]<\/pre>\n<p>The output, shown below, is a perfectly valid aggregated table:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"251\" height=\"271\" class=\"wp-image-93203\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-36.png\" \/><\/p>\n<p>Filtering data using <code>SUMMARIZE()<\/code> is not difficult, but it does require applying the filter to the table that is used as the source for the <code>SUMMARIZE()<\/code> function (with all the associated rigmarole of having to use <code>RELATED()<\/code>) as you can see in the following piece of DAX:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nSUMMARIZE(\r\nFILTER(FactSales, RELATED(DimGeography[CountryName]) = \"France\")\r\n,DimCLient[ClientName]\r\n,DimVehicle[Make]\r\n,DimGeography[CountryName]\r\n,\"Cost\", SUM(FactSales[CostPrice])\r\n,\"Sales\", SUM(FactSales[SalePrice])\r\n)\r\nORDER BY DimCLient[ClientName], DimVehicle[Make]<\/pre>\n<p>You can see the output from this piece of DAX in the following table:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"369\" height=\"250\" class=\"wp-image-93204\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-37.png\" \/><\/p>\n<p>Alternatively, you can wrap the entire <code>SUMMARIZE()<\/code> function in a <code>CALCULATETABLE()<\/code> function, like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nCALCULATETABLE\r\n(\r\n  SUMMARIZE\r\n  (\r\n  FactSales\r\n  ,DimCLient[ClientName]\r\n  ,DimVehicle[Make]\r\n  ,\"Cost\", SUM(FactSales[CostPrice])\r\n  ,\"Sales\", SUM(FactSales[SalePrice])\r\n  )\r\n,DimCLient[ClientName] = \"Glitz\"\r\n)<\/pre>\n<p>The output to this query is shown below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"238\" height=\"160\" class=\"wp-image-93205\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-38.png\" \/><\/p>\n<p><code>CALCULATETABLE()<\/code>, of course, will modify the filter context \u2013 whereas <code>FILTER()<\/code> is an iterator function. For paginated reports, modifying the filter context could be largely irrelevant as long as the paginated report dataset is feeding into a single table or chart in the .rdl file. So, in many cases, <code>CALCULATETABLE()<\/code> could be a simpler way to filter data. And it opens the path to using modifiers (such as <code>ALL()<\/code>, <code>ALLEXCEPT()<\/code> etc) to create more complex filters.<\/p>\n<p>Yet another alternative is to wrap the output from a <code>SUMMARIZE()<\/code> function in one or more <code>FILTER()<\/code> functions, as you have seen previously. However, this, once again, necessitates adding the column that you will be filtering on to the output from the <code>SUMMARIZE()<\/code> function.<\/p>\n<p>So, while it is worth knowing that <code>SUMMARIZE()<\/code> exists, most people prefer to use <code>SUMMARIZECOLUMNS()<\/code> unless there is a valid reason for not using the more modern function.<\/p>\n<h2>GROUPBY<\/h2>\n<p>The final major DAX table function used to return data directly is <code>GROUPBY()<\/code>. Specifically, if you need nested groupings, then <code>GROUPBY()<\/code> is particularly useful, as this technique is not possible with <code>SUMMARIZE()<\/code> or <code>SUMMARIZECOLUMNS()<\/code>.<\/p>\n<p>An example of this requirement is finding the lowest average cost price for a car model per make. The following DAX snippet delivers this back to SSRS:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">EVALUATE\r\nGROUPBY\u00a0\r\n(\r\n  GROUPBY\r\n    (\r\n     FactSales\r\n     ,DimVehicle[Make]\r\n     ,DimVehicle[Model]\r\n     ,\"Avg Cost\", AVERAGEX(CURRENTGROUP(), FactSales[CostPrice])\r\n     )\r\n,DimVehicle[Make]\r\n,\"Minimum Average Cost Per Model\", MINX(CURRENTGROUP(), [Avg Cost])\r\n)<\/pre>\n<p>The complete output is as shown in the following image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"290\" height=\"488\" class=\"wp-image-93206\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/01\/word-image-39.png\" \/><\/p>\n<p>In this DAX snippet, the inner <code>GROUPBY()<\/code> finds the average cost for each model per make, and then the outer <code>GROUPBY()<\/code> returns the maximum model cost per make. Although this approach is generally slower than using <code>SUMMARIZE()<\/code> or <code>SUMMARIZECOLUMNS()<\/code> there are challenges \u2013 such as this one \u2013 that are not possible using other functions.<\/p>\n<p>The main thing to note here is that <code>GROUPBY<\/code> uses <code>CURRENTGROUP()<\/code> to provide the row context for the iteration over the virtual table created by the custom grouping. Be warned, however, that <code>GROUPBY()<\/code> can be slow when applied to large datasets.<\/p>\n<h2>Conclusion<\/h2>\n<p>As you have learned, there is a small suite of DAX table functions that you can use to return data to paginated reports from a Power BI dataset. A good understanding of the available functions and how they can be used, individually or together, will help you deliver the output you wish to display in your paginated reports. The next article will cover several more DAX table functions.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Several DAX table functions can be used for paginated reports when you want all the data, not a summary. Adam Aspin explains how to use DAX table functions for paginated reports.&hellip;<\/p>\n","protected":false},"author":2181,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143528,53],"tags":[5134],"coauthors":[12351],"class_list":["post-93175","post","type-post","status-publish","format-standard","hentry","category-bi-sql-server","category-featured","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93175","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\/2181"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=93175"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93175\/revisions"}],"predecessor-version":[{"id":93355,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93175\/revisions\/93355"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=93175"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=93175"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=93175"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=93175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}