{"id":93356,"date":"2022-02-15T15:35:33","date_gmt":"2022-02-15T15:35:33","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=93356"},"modified":"2026-04-15T19:15:34","modified_gmt":"2026-04-15T19:15:34","slug":"dax-table-functions-for-paginated-reports-part-2","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-2\/","title":{"rendered":"DAX Paginated Reports Part 2: Variables, EXCEPT\/UNION, GROUPBY, and CALCULATETABLE"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"h-executive-summary\">Executive Summary<\/h2>\n\n\n\n<p><strong>Part 2 of the DAX paginated reports series covers the intermediate and advanced DAX constructs needed for more complex report data queries. DAX variables (VAR) allow you to store scalar values, measures, or table expressions for reuse within a single DAX expression &#8211; improving readability and avoiding repeated calculations. EXCEPT, UNION, and INTERSECT perform set operations on DAX tables, enabling combining or comparing result sets. GROUPBY and SUMMARIZE provide alternative aggregation approaches to the SUMMARIZECOLUMNS function from Part 1. CALCULATETABLE applies filter context to a table expression, supporting both simple filter elements and filter tables as inputs. The Tips &amp; Tricks article that follows in the series applies all of these to solve common paginated report development challenges.<\/strong><\/p>\n\n\n<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\n\n\n<p>Paginated reports in Power BI make heavy use of the DAX table functions. In the previous article you revised some of the core DAX table functions-such as <code>SUMMARIZE()<\/code>, <code>ADDCOLUMNS()<\/code> and <code>GROUPBY()<\/code>. This article will refresh your knowledge of some of the remaining table functions such as:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>UNION<\/li>\n\n\n\n<li>EXCEPT<\/li>\n\n\n\n<li>INTERSECT<\/li>\n\n\n\n<li>CALCULATETABLE<\/li>\n<\/ul>\n<\/div>\n\n\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, so the article reviews these also.<\/p>\n\n\n\n<p>Once again, it is worth remembering that many of the DAX table functions have quirks and limitations that can trip up the unwary user in certain circumstances. However, the aim in this article 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 paginated report 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. This article will use the same sample file (<a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/11\/CarsDataWarehouse.pbix\"><em>CarsDataWarehouse.Pbix<\/em><\/a>) that you saw in the previous articles in this short collection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-dax-variables\">DAX Variables<\/h2>\n\n\n\n<p>Delivering more complex (or refined, if you prefer) output with DAX will inevitably involve the use of DAX variables. So now is a good time to introduce you to, or remind you about, DAX Variables. The first thing to remember is that DAX variables can contain:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Scalar values<\/li>\n\n\n\n<li>Measures<\/li>\n\n\n\n<li>Tables (even single row tables)<\/li>\n\n\n\n<li>Variable measures (sometimes called Query Measures)<\/li>\n<\/ul>\n<\/div>\n\n\n<p>What is more, any of these elements can be \u201chard-coded\u201d values. This can be extremely useful for testing &amp; debugging when your DAX starts getting more complex.<\/p>\n\n\n\n<p>Variables can be introduced at any point in the course of a DAX <code>EVALUATE<\/code> statement. However, for clarity, reusability and sheer elegance I often find it easier to specify variables at the top of the script using the <code>DEFINE<\/code> keyword, then use them as required inside the DAX query.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-scalar-dax-variables\">Scalar DAX variables<\/h3>\n\n\n\n<p>To get a better idea of how scalar DAX variables can be used, take a look at the following code snippet (where the parameter <code>@Make<\/code> is input as <em>Ferrari<\/em>):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR CountryFilter = \"France\"\nVAR MakeFilter = @Make\nEVALUATE\nSUMMARIZECOLUMNS\n(\n&nbsp;DimVehicle[Make]\n,DimVehicle[Model]\n,DimGeography[CountryName]\n,FILTER(VALUES(DimGeography[CountryName])\n        ,DimGeography[CountryName] = CountryFilter)\n,FILTER(VALUES(DimVehicle[Make])\n        ,DimVehicle[Make] = MakeFilter)\n)\nORDER BY DimVehicle[Make]<\/pre>\n\n\n\n<p>The complete output from this query is as shown in the following image when typing in <em>Ferrari<\/em> at the prompt:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"214\" height=\"217\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-1.png\" alt=\"\" class=\"wp-image-93357\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>All that this piece of DAX does is to define two variables. The first one is given a value directly, the second is passed a value from the SSRS parameter <code>@Make<\/code>. So, overall, it is worth noting that:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>You can also pass a paginated report parameter to a DAX variable<\/li>\n\n\n\n<li>The order in which the DAX variables are introduced in the <code>DEFINE<\/code> section can be important \u2013 as they must be defined in the order in which they are later used in the DAX query. While most of the time this is not an issue, it can be a factor in large and complex DAX statements.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>You can, of course, add the column(s) that you are filtering on to the output if you want to verify that the filter is working as you expect.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-measures\">Measures<\/h3>\n\n\n\n<p>A DAX variable can also be a measure (a calculation if you prefer) \u2013 as you can see in the following short piece of DAX:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR CountryFilter = \"France\"\nVAR TotalCost = SUM(FactSales[CostPrice])\nEVALUATE\nSUMMARIZECOLUMNS\n(\n&nbsp;DimVehicle[Make]\n,DimVehicle[Model]\n,DimGeography[CountryName]\n,FILTER(VALUES(DimGeography[CountryName])\n        ,DimGeography[CountryName] = CountryFilter)\n,\"Cost per Model\", SUM(FactSales[CostPrice])\n,\"Percentage of Total Cost\", SUM(FactSales[CostPrice]) \/ TotalCost\n)<\/pre>\n\n\n\n<p>The output (shortened) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"457\" height=\"250\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-2.png\" alt=\"\" class=\"wp-image-93358\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>If you are verifying the output, remember that the DAX variable <code>TotalCost<\/code> is returning the cost for all makes for all countries \u2013 and the output table is showing only cars sold in France \u2013 and so the total of the <em>Percentage of Total Cost<\/em> is not 100%.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-dax-variables-for-tables\">DAX Variables for Tables \u2013<\/h3>\n\n\n\n<p>In the context of Paginated reports, you are more than likely to come to rely on DAX table variables as a pillar of your data extraction code. A simple example of how DAX table variables can be used is shown in the following short DAX snippet:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR SalesTable =&nbsp;\nSUMMARIZECOLUMNS\n(\n&nbsp;DimVehicle[Make]\n,DimVehicle[Model]\n,\"Sales\", SUM(FactSales[SalePrice])\n)\nEVALUATE SalesTable<\/pre>\n\n\n\n<p>The output (shortened) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"187\" height=\"252\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-3.png\" alt=\"\" class=\"wp-image-93359\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-variable-measures\">Variable Measures<\/h3>\n\n\n\n<p>Variable measures \u2013 as distinct from merely attributing a measure to a variable can be useful in a couple of ways:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>They can simplify code structures as this approach allows you to isolate calculations from data retrieval.<\/li>\n\n\n\n<li>They are a clear aid to reusability.<\/li>\n\n\n\n<li>They can be very useful for the \u201cdummy\u201d measure that may be required to accelerate querying in the case of wide datasets.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>As a simple example, the DAX shown below uses the MEASURE keyword to introduce a variable measure:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR CountryFilter = \"France\"\nMEASURE FactSales[TotalCost] = SUM(FactSales[CostPrice])\nEVALUATE\nSUMMARIZECOLUMNS\n(\n&nbsp;DimVehicle[Make]\n,DimVehicle[Model]\n,DimGeography[CountryName]\n,FILTER(VALUES(DimGeography[CountryName])\n        ,DimGeography[CountryName] = CountryFilter)\n,\"Total Cost\", FactSales[TotalCost]\n)<\/pre>\n\n\n\n<p>The output (shortened) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"286\" height=\"254\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-4.png\" alt=\"\" class=\"wp-image-93360\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Be aware, however, that a variable measure:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Has to be associated with a table<\/li>\n\n\n\n<li>Must be referred to using a fully qualified reference to the table and the measure.<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-except-union-and-intersect\">EXCEPT, UNION and INTERSECT<\/h2>\n\n\n\n<p>As befits a data manipulation language, DAX includes three core table functions that you can use for set-based data manipulation. These are:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>EXCEPT()<\/li>\n\n\n\n<li>UNION()<\/li>\n\n\n\n<li>INTERSECT()<\/li>\n<\/ul>\n<\/div>\n\n\n<p>I assume that the concepts behind these functions are self-evident, so prefer to show one way that a couple of them can be useful to resolve certain common reporting challenges. For instance, you can apply <code>EXCEPT()<\/code> and <code>UNION()<\/code> to isolate certain records (which you analyse in detail) from all the remaining records (which you aggregate into a single row in the output table).<\/p>\n\n\n\n<p>The principle behind the query that you are about to see is:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Firstly, define an initial table by applying the core filters to return only the required data.<\/li>\n\n\n\n<li>Secondly, from this initial dataset (which is passed to a DAX table variable) extract two separate table variables:<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>One containing the main data, aggregated by selected colours (the \u201cdetail\u201d dataset).<\/li>\n\n\n\n<li>One that aggregates all the remaining records into a single record.<\/li>\n<\/ul>\n<\/div><\/li>\n\n\n\n<li>Finally, output the two subsidiary datasets (selected group and other records aggregated into a single record) as a single table.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Here, then, is the actual code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR SrcTable =&nbsp;  \/\/ The core data table containing all records\nSUMMARIZECOLUMNS(\n&nbsp;DimVehicle[Color]\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nVAR FullList =&nbsp;  \/\/ The selected group of records to isolate\nSUMMARIZE\n(\n  FILTER\n  (\n  SrcTable\n  ,DimVehicle[Color] IN \n        {\"Black\", \"Blue\", \"British Racing Green\", \"Green\"}\n  )\n,DimVehicle[Color]\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nVAR RemainderList =&nbsp;  \/\/ All other non-selected records\nEXCEPT(SrcTable, FullList)\nVAR GroupedRemainder =&nbsp;   \/\/ All other records aggregated\nSUMMARIZE(\nRemainderList\n,\"ColumnTitle\", \"Other\"\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nEVALUATE\nUNION(FullList, GroupedRemainder)   \/\/ The final output<\/pre>\n\n\n\n<p>The output (shortened) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"158\" height=\"123\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-5.png\" alt=\"\" class=\"wp-image-93361\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Of course, the set of records that is categorised (ie., not \u201cother\u201d) can be calculated using a more complex DAX query rather than simply being hard-coded as is the case here.<\/p>\n\n\n\n<p>There is one main point to note here: <code>SUMMARIZE()<\/code> can be used not only to aggregate \u2013 but also to rename the element that is output. This is a useful trick when aggregating a table into a single record.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-groupby-and-summarize\">GROUPBY and SUMMARIZE<\/h2>\n\n\n\n<p>A variation on the theme above is to isolate all records that match a simple filter category, and then aggregate the others into a single record. This time, however, <code>GROUPBY()<\/code> and <code>SUMMARIZE()<\/code> are used in slightly different ways to achieve the same result. This is an example of how DAX table functions can be combined to solve certain paginated report challenges:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR SrcTable =&nbsp;\nSUMMARIZECOLUMNS(\n&nbsp;DimCLient[ClientName]\n,DimCLient[IsCreditRisk]\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nVAR FullList =&nbsp;\nSUMMARIZE\n(\n  FILTER\n  (\n  SrcTable\n  ,DimCLient[IsCreditRisk] = 0\n  )\n,DimCLient[ClientName]\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nVAR GroupedRemainder =&nbsp;\nGROUPBY\n(\n  FILTER\n  (\n  SrcTable\n  ,DimCLient[IsCreditRisk] = 1\n  )\n,DimCLient[IsCreditRisk]\n,\"Total\", SUMX(CURRENTGROUP(), [Total])\n)\nVAR GroupedRemainderOutput =&nbsp;\nSUMMARIZE(\nGroupedRemainder\n,\"ColumnTitle\", \"Other\"\n,\"Total\", COUNTX(FactSales, FactSales[SalePrice])\n)\nEVALUATE\nUNION(FullList, GroupedRemainderOutput)<\/pre>\n\n\n\n<p>The output (shortened) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"256\" height=\"312\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-6.png\" alt=\"\" class=\"wp-image-93362\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>The points of note here are:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The <code>GROUP BY<\/code> actually aggregates into a single row of data \u2013 however it outputs \u201cTrue\u201d (the actual contents of the filter).<\/li>\n\n\n\n<li><code>SUMMARIZE()<\/code> can be used simply to rename output.<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-calculatetable\">CALCULATETABLE<\/h2>\n\n\n\n<p>As mentioned in the <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/dax-table-functions-for-paginated-reports-part-1\/\">previous<\/a> article, nesting filter criteria rapidly becomes wearing once you start nesting multiple criteria applied using multiple <code>FILTER()<\/code> functions. A more practical approach can be to wrap a function that returns a data table inside the <code>CALCULATETABLE()<\/code> function \u2013 and apply all the required filters as filter expressions. So, in essence you are applying <code>CALCULATE()<\/code> filters \u2013 to a table.<\/p>\n\n\n\n<p>This approach will work not only with <code>SUMMARIZECOLUMNS()<\/code> \u2013 as you saw in the previous article \u2013 but also with <code>SELECTCOLUMNS()<\/code> and <code>GROUPBY()<\/code>. The following piece of DAX uses a <code>SELECTCOLUMNS()<\/code> function to define the table that you wish to use as the source table in the <code>CALCULATETABLE()<\/code> function, and then applies any required filters as part of the <code>CALCULATETABLE()<\/code> function:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EVALUATE\n\nCALCULATETABLE\n(\nSELECTCOLUMNS\n(\nFactSales\n,\"Client\", RELATED(DimCLient[ClientName])\n,\"City\", RELATED(DimGeography[Town])\n,\"Credit Risk\", RELATED(DimCLient[IsCreditRisk])\n,\"Country\", RELATED(DimGeography[CountryName])\n,\"Make\", RELATED(DimVehicle[Make])\n)\n,DimGeography[CountryName] = \"France\"\n,DimVehicle[Make] = \"Ferrari\"\n,DimCLient[IsCreditRisk] = 0\n)<\/pre>\n\n\n\n<p>The output is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"337\" height=\"140\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-7.png\" alt=\"\" class=\"wp-image-93363\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>You need to note a few key complexities if you ever adopt this approach:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>You will need a clear understanding of the underlying data model in order to use <code>RELATED()<\/code> if you are using <code>SELECTCOLUMNS()<\/code><\/li>\n\n\n\n<li>There are many subtleties and variations on the theme of filtering data \u2013 some ideas and suggestions are given above (and in the previous article).<\/li>\n\n\n\n<li><code>AND<\/code> logic for filtering is easy when filtering this way. OR logic can be harder (and is explained in greater detail in the third and final article in this series).<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Overall, this technique is best suited to simple list output. Also, it can get complex when using measures (given the intricacies of DAX context), so for this reason too is probably best used for relatively simple list output.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-filter-tables-in-calculatetable\">Filter tables in CALCULATETABLE()<\/h2>\n\n\n\n<p><code>CALCULATETABLE()<\/code> can also be used with filter tables as well as (or instead of) simple filter elements of the kind that you just saw.<\/p>\n\n\n\n<p>The following DAX extract creates a table variable of a list of country names and then uses the table variable as a filter element that is applied by <code>CALCULATETABLE()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR FilterTable =&nbsp;\nSUMMARIZE\n(\n  FILTER\n  (\n  DimGeography, DimGeography[CountryName] in {\"France\", \"Belgium\"}\n  )\n,DimGeography[CountryName]\n)&nbsp;\nEVALUATE\nCALCULATETABLE(\nSUMMARIZECOLUMNS(\n&nbsp;DimCLient[ClientName]\n,DimGeography[CountryName]\n,DimGeography[Town]\n,\"Sales\" , SUM(FactSales[SalePrice])\n)\n,FilterTable\n)<\/pre>\n\n\n\n<p>The output (shortened once again) is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"363\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-8.png\" alt=\"\" class=\"wp-image-93364\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>In practice, this kind of approach can be more useful if the filter table is based on, say, a calculation. As an example, the following DAX sample creates a filter table that dynamically calculates the top two selling makes of car (by sales) and filters on the result:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">DEFINE\nVAR TopSellersTable = \n    TOPN(\n        2,\n        SUMMARIZECOLUMNS\n        ( \n            DimVehicle[Make]\n            ,\"Total Sales\", SUM(FactSales[SalePrice])\n        ),\n        [Total Sales]\n        ,DESC\n    )\nEVALUATE\nCALCULATETABLE(\nSUMMARIZECOLUMNS(\n&nbsp;DimCLient[ClientName]\n,DimGeography[Town]\n,DimVehicle[Make]\n,\"Sales\" , SUM(FactSales[SalePrice])\n)\n,TopSellersTable\n)<\/pre>\n\n\n\n<p>Part of the output from this code snippet is as shown in the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"613\" height=\"363\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-9.png\" alt=\"\" class=\"wp-image-93365\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Here too, you do not have to display the make in the output, but I have chosen to do so to prove that the filter table actually works.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-addmissingitems\">ADDMISSINGITEMS<\/h3>\n\n\n\n<p>While we may have looked at the core, everyday use of <code>SUMMARIZECOLUMNS()<\/code> in the previous article, there are a couple of ways that this function can be extended that are worth knowing about. You may, one day need to use the <code>ADDMISSINGITEMS()<\/code> function to force rows to be returned that <code>SUMMARIZECOLUMNS()<\/code> would otherwise exclude.<\/p>\n\n\n\n<p>This option of <code>SUMMARIZECOLUMNS()<\/code> forces the output of records (rows of data) that would not otherwise be returned by <code>SUMMARIZECOLUMNS()<\/code> when output rows otherwise equate to empty or Null. This is probably best appreciated using a simple example. The following code wraps a <code>SUMMARIZECOLUMNS()<\/code> function inside an <code>ADDMISSINGITEMS()<\/code> function \u2013 and specifies that the date and make should nonetheless be returned if the row is otherwise empty:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EVALUATE\nADDMISSINGITEMS\n(\nDimDate[FullYear]\n,SUMMARIZECOLUMNS\n(\n&nbsp;DimDate[FullYear]\n,DimVehicle[Make]\n,FILTER(VALUES(DimVehicle[Make]), DimVehicle[Make] = \"Trabant\")\n,\"Total\", SUM(FactSales[SalePrice])\n)\n,DimDate[FullYear]\n)\nORDER BY DimDate[FullYear], DimVehicle[Make]<\/pre>\n\n\n\n<p>Running this code gives the following output:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"142\" height=\"161\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-10.png\" alt=\"\" class=\"wp-image-93366\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>To make the comparison clearer, running the DAX without <code>ADDMISSINGITEMS<\/code> gives the following output:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"143\" height=\"46\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/02\/word-image-11.png\" alt=\"\" class=\"wp-image-93367\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\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 \u2013 and how they can be used, individually or together \u2013 will help you to deliver the output that you wish to display in your paginated reports.<\/p>\n\n\n\n<p>Remember that the aim of these two articles is not to provide an exhaustive and detailed overview of all the DAX table functions. The subject is simply too large for that. However, there are many excellent resources available if you wish to delve deeper into the myriad possibilities (and arcana) of table functions. What you have seen here is that you are not limited to <code>SUMMARIZECOLUMNS() <\/code>when returning tabular output from a Power BI dataset to a paginated report, and that there are several other functions that you may find useful \u2013 or even necessary \u2013 in certain circumstances.<\/p>\n\n\n\n<p>However, returning basic tables of data is only one step on the path to delivering paginated reports. You may well need to know a plethora of tricks and techniques to solve real-world challenges. The next article in this series introduces some of the tips that you may well need when creating paginated reports using a Power BI dataset.<\/p>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: DAX table functions for paginated reports: Part 2<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What are DAX variables and how do I use them?<\/h3>\n            <div class=\"faq-answer\">\n                <p>DAX variables (declared with VAR) store a scalar value, measure result, or table expression within a RETURN block. Example: VAR SalesTotal = SUM(Sales[Amount]) VAR SalesTarget = 1000000 RETURN DIVIDE(SalesTotal, SalesTarget). Variables are evaluated once at the point of declaration &#8211; using a variable instead of repeating an expression avoids redundant recalculation. Table variables (VAR TableVar = FILTER(Table, condition)) store a table result that can be passed to FILTER, CALCULATETABLE, or other table functions.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What is the difference between GROUPBY and SUMMARIZE in DAX?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Both functions aggregate a table by specified grouping columns, but with different behaviour for measures. SUMMARIZE evaluates measures in the outer filter context &#8211; it can reference model measures. GROUPBY evaluates aggregation functions (SUMX, COUNTX, etc.) in the context of the grouped table &#8211; it is safer for complex calculations because it avoids the filter context manipulation issues that can occur with SUMMARIZE and measures. Microsoft recommends using SUMMARIZECOLUMNS (for most scenarios) or GROUPBY (for explicit table-level aggregation) over SUMMARIZE with measures.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What is CALCULATETABLE used for in DAX?<\/h3>\n            <div class=\"faq-answer\">\n                <p>CALCULATETABLE returns a table that has been filtered by the specified filter conditions &#8211; it is the table equivalent of CALCULATE (which returns a scalar value). CALCULATETABLE(Table, filter1, filter2) returns all rows from Table that satisfy the filter conditions. It is particularly useful in paginated reports to create filtered datasets from a Power BI model, apply multiple filter criteria simultaneously, and avoid nested FILTER() calls. Filter tables (pre-built tables of valid values) can be passed as filter arguments instead of individual filter expressions.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. How do EXCEPT, UNION, and INTERSECT work in DAX?<\/h3>\n            <div class=\"faq-answer\">\n                <p>These three functions perform set operations on DAX tables with compatible column structures. EXCEPT(Table1, Table2) returns rows in Table1 that are not in Table2 (equivalent to SQL EXCEPT or NOT IN). UNION(Table1, Table2) combines all rows from both tables including duplicates (use DISTINCT or SUMMARIZE to deduplicate). INTERSECT(Table1, Table2) returns rows present in both tables. In paginated reports, these are commonly used to build exclusion filters (EXCEPT), combine multiple parameter value lists (UNION), or find common elements across two datasets (INTERSECT).<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>DAX for Power BI paginated reports Part 2: scalar and table DAX variables, EXCEPT\/UNION\/INTERSECT for set operations, GROUPBY and SUMMARIZE, and CALCULATETABLE with filter tables.&hellip;<\/p>\n","protected":false},"author":2181,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":true,"footnotes":""},"categories":[143528,53],"tags":[5134],"coauthors":[12351],"class_list":["post-93356","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\/93356","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=93356"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93356\/revisions"}],"predecessor-version":[{"id":109872,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93356\/revisions\/109872"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=93356"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=93356"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=93356"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=93356"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}