Transcript for:
Mastering Advanced DAX Concepts

is there anybody here that was not here this morning so many of you okay since it seems that there are still a lot of people that want to see these sessions and there was simply not enough space for everybody we are going to repeat the introduction to that so this morning session will be repeated again this afternoon at 4:40 p.m. until 6:30 p.m. and I have no idea where this will happen oh in regen C see again here it's written it's just me without glasses whereas this session will be repeated tomorrow morning at 1010 a.m. up to 12:20 a.m. okay that is more for the people who are sitting in the back or the people who want to see the introduction to DX or whatever it's a a choice whether to attend the repeating of the session okay I was hoping to skip this slide but too many people were not here this morning now we are going to speak about Advanced Ducks uh just to set expectation this session was listed as a 400 level session on the uh on the conference this is far from being a 400 Lev session it just the continuation of the introduction to that so we will basically speak about evaluation context row context filter context and then use these Concepts to build some calculations some iterations some iterations and sometime intelligence calculation so it's far from being a 400 level session is just a continuation of the introduction to backx just to avoid uh people that expect to see fireworks or strange stuff which is not going to happen at all I'm Alberto Ferrari I'm a consultant with marus I founded sqlbi a few years ago www SQL bi.com and there if you search something about Dux it's very likely that you will find the website because there we publish all our thoughts and papers and stuff about the Dax language the content of the session of course is introductory part of the definite guide to Dux let's go to slides stri this session is no two slides thanks two slides stri this session is much more theoretical than the previous one so we are basically trying to understand and get a good a good feeling about what evaluation context are and this is the only complex part of Dax and in my experience it's the point where everybody stops learning Dax and try to basically they stop grasping Evolution context is hard is much more complex than it looks at First Sight but remember da is simple it's not easy but it's simple so there will be nothing that I will tell you that is hard to understand everything will be extremely simple the point is that when you put everything together then it becomes a nightmare and it takes some time before you U you get used to it so evaluation context are simple concept just a bit hard to learn we will see the basic introduction and then we will see several examples if you're serious about duck you will probably need to go farther than what we will be able to cover today uh in learning more details uh which are in the book there are in advanced workshop and in advanced courses that's it let's go straight and let's speak about what is an evaluation context this slide is a question for you that is uh what is this formula going to compute and it's a very simple question so don't expect any any TR the sum of sales amount that is fine for which rows all of the rows and that is the most natural answer but if it is computed in the sales for all of the rows why is it that I mean it's clear I put it in a pivot table and I get all the rows but why is that as soon as I slice by color I no longer see the sum of all the rows for each row I see the sum of only that color that means there is something else which is working under the cover the point is that the formula says compute the sum of the sales amount column in the sales table for all the rows but there is something which is is the context under which the formula is evaluated that hides from the formula several rows and basically we can say that when we are in the first row black the formula tries to compute the sum of all sales but the context under which it is evaluated prohibits the formula to see anything which is not black so what do you put in a pivot table basically filters the content of the model showing only a subset of the data and this something is basically the context under which the number is computed every number is computed into a context and the context defines the final value that is returned by formula so say in other words the formula describes the algorithm so what need to be computed but then the context defines on which subset of the data the number will be computed and if you don't what you put in the filter of the pivot table and finally what you put on a slicer if you use one or many slicer and you reduce the number of rows visible basically you are modifying the filter context okay let's see an example we have already seen some of these slides this morning I just want to repeat the their animation and this time I will be more precise in using the current names if you want to compute for example this value 64 the small and green 64 has a filter context and the filter context is green because we have green on the rose then it's large because large is on the columns and finally it's internet because internet is slice is filtered by the slicer so the only roow that contains internet small internet green and large is the the sem last one and so this is the value shown there whereas if you go on the other row which is small then you have green small internet and the last one is the only one that satisfy the condition okay what about the grand total well at the grand total this value 240 240 has lost any filter from the sides because there is no longer any filter on the column there is no any no longer any filter on the rows yet we have a filter on internet coming from the slicer so you should think at the grand total has being computed as any other cell by scanning the model using a specific filter context don't fall into a trap of thinking that the grand total is computed by summing or by aggregating values which are already computed row by row because this is not what is happening the gr toal is Computing doing another scan of the data model with a different filter context and from a theoretical point of view every cell of your pivot table or your report is computed during a scan of the model in a different filter context make sense cool now the filter context is defined by what you put on the row what you put on the column field column report and slicer anything which is outside of the filter context simply does not exist it's not there you cannot see it in any way and it's defined automatically when you create a pivot table the user or you if you are your user of your model simply generates the pivot table and generates filter context in this way of course you can generate it too by using specific Dux functions and we will learn them during the course of the day but that is the first kind of context there is another kind of context that we already encountered this morning and it is the row context the row context is the concept of having a current role we spent a lot of time this morning saying oh do we have a current row here in more current terms we should say do we have a row context open right now if we have a row context it means we are iterating over a table and if we have a row context we can evaluate column column values if we don't have a row context we cannot evaluate column column values so it's Define automatically when you create a calculated column you don't need anything the filter the row context is already there whereas it is explicitly introduced by sux filter average X any X function or any iterator will introduce a row context and it's needed to evaluate column values it's the very simple concept of current row make sense is everything utterly simple yes I didn't I hear yes yes it is the complexity is not coming right now so let's give it time so let's look in more detail at what happens for example if you do a sumx over orders of quantity multiply by Price this is the title is the measure we want to compute and imagine that I want to compute the ground total 592 what is the first thing that the engine needs need to check what is the first thing that you need to know in order to compute this value louder I I know very well the technique that I use when I was a teenager of answering with a low volume of voice so the teacher doesn't hear and say oh you must have said it right that's not going to work I'm too old for that and by the way I'll repeat your answer in a few seconds which is right of course uh it's nearly the answer to all of the question I'm going to ask this morning this afternoon so now that you're done it's very easy the third thing that you need to check is the filter context because this is filtering the old data model so you need to understand what the filter context is here and we have no filter on sides no filter on color still a filter on internet so the filter context will contain only the last four rows and this is the data set that is visible when your formula starts to compute the value then some X kicks in and says oh I need to iterate the orders that doesn't mean iterate all the orders but only the orders visible in the current filter context that is four rows it goes on the first row that is it opens a row context on the orders goes on the first row performs the calculation 16 multip by 7 put the result as side and then goes on iterating over the table and Computing the value one at a time putting them all one on top of the other at the end of the calculation it sums everything does a sum an average or whatever aggregation is required and then put the result there okay and that's what happens whenever you compute the value cell by cell remember that there are always two context and the two context have nothing to share there is the row context and there is the filter context the two are different and they do different things the row context provides a single row whereas the filter context filters the data model I told you that I was going to be pedantic and I'm just starting both are what we call evaluation context so the evaluation context are only two row context and filter context the row context might not exist in a standard measure there is no row context and the filter context might be empty meaning that you can see everything at the grand total of any pivot table or if there is no filter at all but this seldom happens because you are not interested only in Grand totals of course I'm so interested in filter context because working with evaluation context is the key to most if not all of the measures and calculation that you will do with Dux and learning how to master them requires some time let's see again with the corrent terms what is happening when you filter a table the measure now is a bit more complex it's some X over filter of orders where orders price is greater than one some X requires the result of filter filter needs to scan the orders but it cannot see all the orders it only see the orders which are visible in the current filter context and for imagine that we are at the total for green the filter context is green internet and because it's green internet it only filters the last two rows then at this point filters filter opens a row context on top of the orders table it iterates over the uh two rows the first one survives the filter the second one does not survive the filter so it's going to be removed by filter and finally some X goes on iterating over the result of filter and produces the final result the question for you the first one is do we we know we have a row context here we already discovered that this morning but do we have a filter context here in the Box the order price greater than one I always need to show that twice I see somebody saying yes somebody said I don't want to answer and what is the filter context at that point greater than one greater than one might be any other answer the previous filter other kind of answers the current row so basically ask yourself do you see anybody that is able that changed the filter context greater than one is a condition that is created by filter by iterating over orders and is used to remove rows from a table okay the filter context was there at the beginning and some X is not changing the filter context filter is not changing the filter context the filter context only hides rows from the model so it removes rows from the model and no not a single function here is able to change the filter context they will feel the filter context they will not be able to go outside of it but they are not going to change so right now there we have a filter context we have a row context introduced by f filter over orders and we have a filter context which is the original filter context of the pivot table because not a single function here changed the filter context no ERS price greater than one sorry the question was does this filter context excludes rows that are greater than one or smaller than one ones that are or one that are equal to one no the point is um the filter context is created by the pivot table okay filter and some X do not change the filter context that's not what they do sux iterates over a table and does a calculation filter scans a table and returns a result but neither samx nor filter change the filter context okay so the filter context is always the same nobody changed it filter returns a table so it scans orders and returns as a result a table that table does not contain rows where the price is 3ly greater than one but the filter context still contains those rows they are there if you want to access them they are there it's just filter that is not returning them okay do not make confusion between filter context and the iterative nature of table functions or table functions that return table they have nothing to share the filter context is a concept which is a huge filter on top of your model while filter iterators and all this stuff have nothing to do with the filter context we will find the function that is able to change the filter context but it's coming in a few minutes yep is it right to say that the filter context is applied to table function before they are executed yes you can think the easiest way to think at the filter context is when you read the Dax expression replace all the table names in your mind with by adding as visible in the current context so this is the so saying filter the orders visible in the current context and return me the ones where the price is strictly greater than one okay it is not changing the filter it's just reducing the number of rows we spoke about all earlier and again I want just to be pedantic with all all does not change the filter context all ignores the filter context which is something totally different from changing it so when you want to evaluate that cell the filter is still green internet so the last two rows or wakes up and says oh I should see only two rows in the orders table but I'm all I'm much more powerful than filter contest I'm going to ignore everything and so I always return everything okay that said during iteration here samx opens a row context on all the orders so it sees all the row orders it computes orders amount for every row but keep in mind the filter context is still there nobody changed the filter context one thing which is a not unique about row context but it's very it happens typically only with the row context is that the row it itation can be nested you can do an iteration and then inside the iteration you do another iteration and then inside that iteration you open another one and that's totally fine you can do as many iterations as you want but that means that you iterate over categories then over the products of the category and then over the sales of the product of that category so right now when you're here at the Ellipsis there are three different row context open on three different tables and again there is nothing wrong with with that but you have a row context on category another row context on product category and another row context on the sales table all of them are leaving at the same time that is not a problem and it creates no ambiguity at all now the next point that I want to do with you is an exercise that we can do I think the easiest thing we can is doing that together by throwing IDE instead of writing on your PC it's probably better if we reason about it together imagine that I'm a customer and I ask you to create a i you have a table contains product key product name list price and I want to have a column a new calculated column in the product table that contains the ranking of the product based on the least price so the most expensive product should be ranked number one the second most expensive product should have a ranking of two the third most expensive product should have a ranking of three and you got the game it's just ranking byproduct rank X makes sense did I ever speak about rank X so from your point of view it does not exist beside you can solve it with rankx but with the tools you have so far that does not contain Rank and anything like it how would you compute such a measure such a calculated column maybe we can the problem is clearly set I guess so you know what we want to compute we can move to the demo machine and then build the measures together so we only need to go on the product table we can get rid of some columns just to have a bit more real estate okay let's remove also the unit cost so we now have more space and we can go to modeling new column ranking on unit price oh don't write because I want to hear your voice now it's not a matter of writing it's a matter of thinking how would you compute it count the number of rows that's the first time I get the first answer as the first the first time Y in order to compute the ranking typically people answer saying oh you take the distant count or you take you sld them by list price in reality the algorithm is much more simple what you need to do is simply given a product you simply count the number of products that are in the same table with a price Which is higher than yours make sense if this number is zero that means you are the most expensive product if this number is one then you are the second most expensive product if this number is two blah blah blah blah you got the point okay we know how to count we know how to filter we have everything we need to do it nearly everything so we need to count and we do a count rows we don't want to count all the products we I hate you we filter the product table before writing the full measure Let's uh do something easier just to check that the syntax is right where is greater than the product where the unit price let's say is greater than 100 this is going to return for to return for all the rows the same value that is the number of products which have a price greater than 100 okay and if I hit enter I get the result 1728 there are 17 28 products which have a price greater than 100 now what I need to do is now replace that 100 with the current list price make sense say yes or no just keep cool and I'm in trouble right now now because um do we have a row context here where the cursor is blinking who said no we don't have a row context there filter remember is an iterator we have first of all we have a row context coming from the fact that we are creating a calculated column and then we have another row context which is created by filter while it iterates over product make sense so we have in reality two row context open at the same time they are nested one into the other the problem is that the row count when I write product unit price to which row context I'm referring to cont the row context coming from Filter which is the innermost the nearest one so I have two row context both are open on the product table and the innermost one is hiding the previous one because they are on the same table and so when I write product unit price I'm referring to the price of the current lated row by filter over product and what I want to do is compare this value with the row context that is outer the outer one the previous one make sense and the point is that I cannot write product unit price here I need something different there is a function that has one of the worst names that could have been chosen which is earlier keep in mind that the original name of earlier was outer and as outer I understand it much better because I have a nesting of row context and they go out one level instead they call it earlier what earlier does earlier simply give you access to the outer row context so it says I want the value of the unit price but I don't want the value iterated by filter I want the value iterated by the previous iteration by the outer iteration make sense and if you write the formula in this way you get something that reminds at least a ranking what's wrong with you oh I need to sort by unit price wise we don't see values uh okay well it's not really correct because we need to add one now we see one one one and these are all these product have the same unit price and this is showing one for all the rows okay and then for the first row that has a slightly lower price we see 15 because there are 15 ties before that make sense now imagine you propose this solution to your customer and you say well of course 15 comes after one so it's a good ranking solution if the customer says Okay I want to meet him because he need to be a really good customer typically people say no I don't want to see 15 I don't mind how many many ties are there I want to see two there because ranking is one two three so how would you change this formula to compute two instead of computing 15 louder this thing of what of the rows of the prices okay elaborate on that I mean this thingle unit price is not an answer yet a bit more louder I'm also far what is function is doing it's Computing the products which have a price greater than yours we are not interested at all in the products we are interested in the prices higher than mine now do we know a function that is able to return instead of all the products all the values of the product unit price the answer is yes but now I need somebody to tell the function all all you iterate overall product unit price and now this is completely different algorithm it does it says oh get all the values of the unit price iterate over them and now no matter how many products have the same price they will only count for one row in all unit price and so the result will no longer be 15 at this point the result will be two so I have one with all the ties and then two two 3 four five and so on makes sense so there are two things here to know the first one is the usage of earlier and the second one is the usage of the the way of computing these values in Dax now keep in mind that earlier is considered one of the most complex functions in Dax because I don't really know understand because it's not easy to to grab at first s but the point is that if you think in terms of row context that can be nested the earlier is pretty simple to use and understand nevertheless everybody hates earlier including myself so you can get rid of earlier if instead of using earlier you use variables you remember that we spoke of variables this morning and what you can do is Define a variable and you call it current Leist price which is equal to the product unit price once the variable is defined I'm doing damages then you return account rows of fi of all product unit price that are greater than current list price if you write it in this way it's much easier to read it's much easier to understand and probably it gives also a sense to the previous version we did with earlier because if you write product unit price outside of filter then the only row context which is open is the one of the calculated column so that is the product unit price of the current row whereas product unit price inside of filter is the current lated row by filter over all product list price so one now that we have variables you can forget about earlier and start using variables instead because they make the code much easier to write to read and to debug and beside by giving names to the values that you use the code is also much better to to use and write variables are available in powerbi and I think they are available also in SQL Server 2016 they are not available in SQL Server 2013 okay question yeah so will this variable beable globally cross our whole powerbi file no the variable is defined locally the variables are defined locally to where you use them so any DX expression can start with VAR and Define a variable which is local to that expression but then it's lost outside of it we would love to have Global variables but they are not there yes well in that case using values all or this thing they all pro provide the very same result because uh the only difference will be the filter context but we don't have any filter context here so that's not an issue okay let's go back to the slides please now this is just in the slides to as a reminder for later uh we write the version with earlier or the version with variables which is much easier to uh read and use but it's available only in the newest versions of the Dex language now it's time to speak about calculate I typically call calculate the queen of all Duc functions so somebody already heard it and uh I call it the queen I really think at calul as a queen not as a king because I I really think calulate is a woman that means you will never totally understand her yet it's incredibly powerful and I was scared before saying something like this in the US I know you are met with sexual harassment but I mean being Italian I'm not expected to even understand what sexual harassment is so it's fine and beside after what the XBox guys did last few days I don't worry anymore about what Microsoft would ask me why calculate is powerful and why calculate is complex because calculate is the only the single only function in DX which is able to modify the filter context no other function have the same power calculate is the only function that is able to take a filter comp context modify it and create a new one remember that all the other function obey to the filter context calculate is much more powerful calculate is able to modify it and the syntax of calculate is extremely simple you write calculate then an expression the expression can be whatever you want any Dux expression and then you provide a set of filters one after the other all the filters are evaluated put in a logical end together and used to create a new filter context we will see a lot of examples of calculate usage so what you can do is uh write an expression which is by the way extremely easy to read like this one calculate some of sales amount where sales amount is greater than 100 this is different from Filter this is very different from filter because now what we are doing is calculate wakes up it evaluates sales amount greater than 100 it builds a filter that says let's get rid of all the rows in sales amount which are not greater than 100 then I will apply the filter to the model as a filter context and finally I compute sum of sales amount in the newly created filter context okay so this time sum of sales amount is still the sum of the sales amount column in the sales table for all the rows visible in the current filter context where the filter cont is the one generated by calculate adding the condition that sales amount need to be greater than 100 okay so it ignes the preexisting cont when constru this new not not yes and no uh it will replace any existing filter on the sales amount column only any other filter will be kept alive but Pro if you reach this formula when there is a filter already applied on sales amount that filter would be thrown away okay the first thing that you need to always remember because it's important and it would have help me a lot at the beginning is that the filters of calculate are not con conditions you read them as conditions because it's easier and it's simple to read them as condition but in reality the filters of calculate are tables you can pass to calculate a table as a filter where the table identifies the good values for the column so when you write calculate Su of s sales quantity Where unit price is greater than 100 this is equivalent meaning that is internally converted in into a more complex expression that says calculate the sum of sales quantity and filter all the unit price where unit price is greater than 100 and the important part of this filter is this all it's not filtering the values of unit price it's filtering all unit price so it first removes any filter from the unit price column and then it replaces the filter with a new filter that satis that shows only the rows where the unit price is greater than 100 into the lower one yeah from a functional point of view from a performance point of view they are the very same this is just easier to write and to read but when using the or using that it's the very same it's uh just syntax sugaring it's easier to write a condition but in reality it's converted into a table okay here we have a row context which is introduced by filter over all then filter terminates its execution and when filter terminates that set of values is used to build a filter context that is then used to compute the sum of series quantity okay so for example if you want to compute the sum for all the colors you simply compute calculate the sum of sales quantity for all product colors now it might be useful to go back to the demo machine and write a couple of measures so we see the result of computing these values can we switch back to the demo machine please okay I did some damage fix this okay so easy just click on fix this and that's it so now we have an empty report let's remove some stuff in reality let's remove everything it's easier okay let's put the color for example and we take the sales amount okay so we have sales amount divided by color sales amount should be a measure I guess yes it's a measure that computes the sales amount now let's create a new measure which are sales in red uh we can do that from here new measure and let's call it let's call it red sales which is equal to calculate sales amount where product color equal equal red now before you hit enter otherwise that's so easy to answer what number will I see our result always the sales of red products or only the Val the red value when red is already selected in the pivot table or in the report who says the same value everywhere who says red only where red is selected you and half because the the hand doesn't go all the way up then it's full of blank results blank values think of what the calculate will do calculate will wake up and say oh I'm going to repl I'm going to evaluate product color equal red so I filter all the colors and I return only red so imagine that you are Computing the value here for brown When You Reach this point the filter context contains Brown calculate wakes up replaces the filter on color brown with the new filter on red and then it computes the sales amount so the final result that you will obtain is always red of course I need to add the measure now I really wish I knew where I def find it can you see red sales anywhere oh here it is perfect place where to put it okay and you see that you see 126 for all the rows okay keep in mind that if you add more filters for example you filter by brand to the filter on brand is not overridden so what you see in Red Sales right now are the field are here sales amount is the sales of black lare while 19450 is the sales of red lare makes sense because the filter on the brand is still active we are adding we are replacing the filter only on the color make sense now imagine that you want to you want a different measure because you don't like the behavior so let's get rid of this and instead of seeing red sales in this way customer you told me yes I now want a different measure that shows red only when red is already selected otherwise I wanted to show a blank how would you modify the code to make it work to show only red on red and avoid showing values when red is not selected go ahead okay so use filter I'm your finger just tell me and I will write I don't know okay the idea was not bad okay sum of sales amount is already here sales amount is a measure that computes the sum of sales amount elaborate on that imagine that you are writing it that is not going to work go ahead speaking I I hear hand but just go speaking don't worry and you try that on your PC filter I guess it probably is going to work even without anything okay if I write it in this way apart from the fact that product is a keyword and you can imagine how I hate product from being a keyword because it appears everywhere now it's working so it's filtering only the products and shows red only shows s only where red is there why is doing that where did I told the engine what is happening to obey the filter context I'm looking at you you provided the answer so the point is why is it working it is is to okay but where is it doing it there was a hand somewhere there there you're saying fi of the product and in your mind you always need to replace product with the product as visible in the current filter context so this is not filtering old the products it's only filtering the red products if R is selected and it's filtering the black products if black is selected it's filtering the green yellow or whatever color because you wrote products there if you instead of product you write all product then you get as a result the previous result because now are saying don't filter only the currently visible product filter all of them and so no matter what you choose as a color it always shows the sales of red make sense okay now let's go back to slides please so you can write for example sales of red products by Computing here I'm writing some of sales quantity on the demo I use the measure but that's the very same you can write whatever expression whatever DX expression you want and that returns you some result it might be interesting to know that we did it very naturally but it hides some complexity the fact that we place the filter on the product color and then we perform the calculation on the sales table so the filter is done on one table but calculation happens on another table so there should be some kind of automatic filtering happening and we will go back to that later since all the conditions that you put in calculate are put in a logical end together if you need to use a nor condition then you need to merge into a single condition two different sets so if you want Italy and France together you do a calculate some of orders well that is a mistake quantity Where count is equal Italy or France and these two conditions should be put in the single condition because if you split them as two different condition they will put in a logical end and as a result it will always be empty because there is nothing which is Italy and France at the same time make sense and all all not all but in general calculate is powerful and it is very useful because it uh lets you remove and replace filter in a very GR granular way you can remove and replace filters at the column level where every column can be handled separately for example in this slide I'm creating a measure that I called all all channel that removes the filter from the channel but still keep the filter on the sides and the filter on the color so filter on sides and on colors are kept filter on channel is removed and if you remember the slides we see we saw earlier earlier there there was always the same value for all the cells because using all at the table level and without having the power of filter context you typically have the option of removing all the filters at all or uh keep all of them whereas with calculate you can remove filters from only one part of the filter context working in a very granular way so it's extremely powerful how does this work when you write calculate Su of orders orders amount for all orders channel on this model imagine that you want to compute this value the first thing that you need to check is the filter context the filter context there is a green internet because we have a filter on channel and we have a filter on green so the on the last two rows then calculate wakes up before Computing this part it evaluates the new filter that says oh you want to re you want to consider as valid values any value for the channel so I will compute all the values of the channel I create a this T of those and so I get store and internet then I remove the filter from the channel but I will keep the filter on color because the filter on color is still there and so it adds these two rows to the filter count context so that sum X does not iterate only two rows but it will iterate four rows make sense yes okay so calculate is extremely powerful the only draw it's not a draw it the only point is that being able to modify the filter context you need to check at different points and a different part of the formula what is the current filter context so what does calculate do calculator can partially replace filter context modify the filter context and can replace a old table or it can replace a single column and it's the only function that works on the on the filter context it has another small thing but we will see that later in half an hour I guess we saw before that you can apply the filter on themm product and then compute values on the sales table so there should be some kind of automatic filtering happening and this is the part that we need to understand imagine that you have a data model like this one that's a very simple star schema that contains as usual sales product customer uh customer sales product category and subcategories and then finally categories it's a snowflake now imagine that you create a filter context on themm product category do you expect the filter to be propagated to other table or do you expect the filter to stay there and filter only one table it might say there it might be propagated we don't know that yet but we'll Discover it in a while and that's is for the filter context what about the row context imagine that I start iterating the sales table and if I iterate the sales table what I I expect to see in the product in the customer and in all the related tables I will have a current row also there because I'm iterating or the current row is limited to only the uh table on which I have an open row context since we have two kind of context the row context and the filter context and we have two roles of a table in a relationship I can be on the one side or on the many side we have in reality in total four different cases to study row context on the one side row context on the many side filter context on the one side filter context on the many side okay so there are not too many it's easier to run through them and understand exactly what Dax defines for each individual Point scenario the row context we have already seen it this morning but it's easy to repeat it the row context is the easiest one because the row context simply does not care at all about relationships if you create uh imagine that you have a relationship between uh orders and channels and in the orders table you write orders amount multip by y minus chinel discount even if there is a relationship we already know this doesn't work we tried it this morning and it simply doesn't work if you want to let the row context be propagated through relationships then you need to be explicit about that and you need to use the related function the related function related basically takes the current row context follow a chain of relationships and reaches the target table creating a new row context on the target table it looks very complex in reality it's just a relationship to for so it's more complex to describe but in reality it's extremely fast so related when you are on the many side and you want to reach to one side and on the opposite side we already know you use related table related table lets you get access to um a table containing all the rows which have a relationship with the current row context so the row context is pretty simple because it basically totally ignores relationships and if you want propagation to happen you need to be explicit about that the filter context on the other hand has a totally different Behavior the filter context propagates through relationships and it does it in two ways depending on the version of analysis Services you are using in s 2013 and 20 not s server in power pivot up to 2016 it propagates only from the one side to the many side it does not propagate from the many side to the one side in powerbi and in analysis Services 2016 but not in power pivot you have the option of defining which way the filter context will propagate and so you have more power and you can Define more complex Expressions but the point is that the behavior is totally different from the filter context let's focus on the easiest part which is let's stay with power pivot or with the power pivot Behavior and the filter context propagates from the one side to the many side but does not propagate from the many side to the one side so imagine that you have a data model which now is a bit more complex than before because it contains two tables and the two table have a relationship we have cities on top and orders at the bottom and we have a slicer on the continent continent is one of The Columns of the city's table we have a slicer on the channel and then on the rows I have the color and on the columns I have the sides okay now the slicer on the continent is selecting North America because it's select in North America it's a filter context which is applied to the city table and shows only North America so there is only single row visible in the table but it's a filter context and a filter context as soon as it sees a relationship it jumps on it and propagates over all the remaining all the related table so it will reach the sales table and filters only the rows which belong to cities in North America finally that number will be put in the pivot table with other slicers and filters make sense if you think of that it's very intuitive it's very natural and it's the very reason why pivot table work if that wasn't working you couldn't filter a product and see the sales of only those products that happens only because the filter Contex is propagated automatically from the one side to the main side now we have selected North America imagine that instead of selecting North America America we select Europe if the filter the slicer selects Europe then on the uh City's table we no longer have an individual Row one single row now we have many rows which are visible because they are all the cities belonging to Europe doesn't really matter how many rows we have selected here we are on the one side of a one to relationship so the filter Contex jumps on the relationship and will filter other table so it will show the three rows in the orders table that are related to visible rows in the cities one and then because there are still filters on green red large small all those numbers will go in the right place depending on other points of the filter contact but the important point is following this jump from the city to from the column in the city table to the city table and then from the city table stride to the F table and that happens automatically you don't need to do anything but that happens automatically because it's a filter context so as you can imagine whenever calculate creates a new filter context and applies it to the data model this propagation happens automatically and this is where calculate starts to make everything a bit more complex because calculate creates a filter put a filter on the table but that filter can prop propagate in different places of the model Having side effects that are not hard to predict but you need to take them into account to understand exactly what is happening and what number is being computed by your model okay let's try it again okay cool you know it's after dinner after lunch I'm worried that you start sleeping at any time and besides this is not really soft so so this is the same diagram we had before the filter context propagates following the available direction of each relationship so in power pivot you can only follow the relationship from the one side to the many side in power bi desktop you have the option relationship by relationship to tell how the propagation happens as a very simple word of warning keep in mind that b directional relationship these way are called the relationship that filter both ways in powerbi are probably one of the most dangerous features that Microsoft ever built into a modeling tool because it's extremely extremely simple to build a model that generates number that have no meaning at all because of this propagation of filter so you start generating a model and it's very very easy to build a model that computes number that the user simply cannot understand so before using B directional filtering and thinking that you can enable it everywhere and everybody's happy think more than once and study really carefully what is happening with your model because I have seen I do it for my job I created model which are really impossible to to understand you look at the number say well I have no idea what is being computed now because the F the chain of relationship is so complex that uh it's no longer human readable anyway for simple star schemas like this one everything is much simple because if you put a filter on di prodct category you are on the one side of a one to man relationship so the filter is propagated to di product subcategory from dim product subcategory it is again propagated to di product and then from di product the filter is able to reach fact sales and all of this happens automatically just because it's a filter context now that is in fact sales it stops the filter does not go to themm customer make sense it's useful to see that if we go back to the demo machine please we can easily take a look at the behavior by creating a very simple measure let's get rid of red sales and let's create a new measure which is seem simply count rows uh let's call number of customers which is Count RS of customer okay now this measure is Computing the number of customers if I get rid of the color and I put for example the maral status and then the number of customers you see that I see numbers which have some meaning 10,000 are married no 10,000 married eight are single and 385 we don't know so these numbers make sense because the filter is applied to the customer table I'm Computing values on top of the customer table and everything is working fine but if instead of filtering by marital state I filter by product color you see that the number here is always the same and if you look at the data model the filter is on the product table one side of a relationship so the product table will filter sales but then sales has a many to one relationship with customer so the propagation stops here that is the filter on product does not reach the customer okay and this is all what you have in power pivot in uh powerbi you can choose the cross filter direction that can be single or both single means that propagation happens as in power pivot both means that the filter is able to propagate from the many side also to the one side and with this setting now you see that this number is totally different now this is the number of customers that bat a nure product or a black or a blue brown gold or whatever okay so if you enable B directional filtering then the filtering happens everywhere if you keep it disabled then the filtering happens only from the one to the side what are the risks of enabling the risks well the point is first of all by creating by enabling B directional filtering everywhere in a star schema you don't have an issue so if you bu if you build a pure star schema you can enable it everywhere you are only paying a very high price in term of in terms of performance because applying the filter from the one to the many side is very fast yes from the one to the many fast applying it from the many to the one is much slower and uh basically you are transforming all the relationships in many to many ones and many many are extremely slow so the the the model might be slower than before but this is a price that you might be willing to pay so you have a few hundred millions Rosa the model is pretty simple stri calculation so you can enable it everywhere the point is that I don't have any pen here to write it down I don't don't have slides ready but it's very simple to create Loops in the set of relationships so if your model is not a star schema but it it can only just contains two fact tables or it can contain a diamond shape or it can contain multiple relationship between tables and you enable B directional filtering everywhere you very easily reach a scenario where uh the model becomes ambiguos so the engine does no longer know what to filter because it can follow too many paths and so you put a filter on one table and that filter can go in different places and reach through different paths the same other table so it doesn't know which road to take and in that case the engine will tell you I'm sorry the model is ambiguous you need to disable B directional filtering somewhere or you need to remove uh you need to disable one of the relationships When You Reach this when you reach this point it's very very easy to create models where the path followed by the relationship is not the most intuitive one and it becomes a bit more complex I wish I had the slides to show you but unfortunately I don't have them prepared it's not something we can discuss just by looking by imagination but if any of you is interested in that St we can stop uh outside later and I will show you the details of when this becomes uh dangerous I just don't have my PC here so I cannot show that so you are asking me why the number I need to see it otherwise no that is number is okay 18 okay and if I go back to the previous one okay oh this number is larger than the individual ones because I have a lot of customer who never bought anything from me so what I'm seeing here the filter started from Azure a is hateful let's say it started from Blue so it starts from Blue it reaches the fact table then the fact table will filter the customer table so you are seeing only the customers who both Blue Products okay at the grand total the filter on the color is gone so there is no filter on the color that is there is no filter on the F table there is that is there is no filter on the on the customers so you see all the customers either they bought anything or they didn't make sense and the two numbers of course are totally different so you cannot grab them by by some and keep in mind that this is always a many tomain relationship so it's very likely that the same customer both bought black and pink products and so it will appear in multiple places so you might find a lot of scenarios where the grand total is larger than the sum of the individual rows because it's a m tomain relationship and it's something you need to get acquainted to it's not uh extremely intuitive at the beginning okay let's get back to slides than y so if I want the number to I need to change my measure to make that I count of customers who have yomer and they also bought blue so add up well in that cas case you need to iterate over the colors and then sum the individual values if you want the total to match the sum of the individual ones which doesn't really make a lot of sense do it's already happening the distinct on the customer you count the number of customers have at the grand total level but at the color level when it's breaking it out it's still can tell you thater thisal okay one of the consequences of how calculate works is that you cannot write expression like the first one apart from the fact that it's totally stupid to check that quantity multipli by two is less than the price but forget about that the point there is that we are using in two different columns from the same table in the same expression now imagine that I reach this calculate with a filter on quantity or with a filter on price for whatever reason what is calculate expected to do it can replace the filter WR quantity it can replace the filter on price it can replace both of them or it can replace none of them there are too many options and they went for the easiest one one which I agree is the correct one that is you cannot even write this expression if you try to write it the engine will tell you that the your references two columns in the same Boolean expression in calculate and this is not allowed if you want to do a test like this you need to move to a table to a filter on the full table that says filter the orders only the ones visible in the current filter context and return me the one where quantity multipli by two is less than the price so you apply a filter on the table and now it's your responsibility to decide whether to remove or to keep filters on the uh table calculate doesn't do that for you okay the last thing about calculate is context transition context transition oh my God context transition is a beautiful feature that either you will love or hate typically at the beginning you will hate it but then after some time you start loving it Cal does another important task that is whenever it is executed inside a row context it it will transform the row context into an equivalent filter context and then it destroys the row context the way I think at it is that calculate hates row context so she wakes up and says oh is there any row contacts around if yes I'm going to destroy them but since I'm a fair function and I don't want to break your calculations I will replace them with an equivalent filter context so that after I executed only row only filter context are around all the row contexts are gone if we go for a while for to the demo machine I want to show you I want to show you okay a very simple test don't do that on your PC this time I will ask you very very simple questions and I expect you to answer without looking at the result because otherwise that is too easy okay now on this data model we are interested only in sales and product s okay so forget about all the rest Focus only on sales and products and I will create a new column in the product table I call it test which is equal to sum of product unit price and that's it it's a very simple measure but I'm hearing fingers writing so that that is the column it's a calculated column it contains sum of product unit price and the question for you is what number will appear here as soon as I hit enter the same number for every row we have the same number for every row as an option we have the same value that you see there so the same number any other option typically the three options are the same number for all the rows the same number of the of the given row or error who says the same number for all the rows who says the same number the for the given row oh few people cool what what sum does s the sum of the product unit price for all the rows which are visible in the current filter context do we have a filter context here no we have a row context who cares about the row context some ignores the row context some uses only the filter context and so as a result you see the same number for all the rows which is the sum of all the products okay and now we call our best friend and say oh now let's try calculate some of product unit price what is going to happen louder we have a row context calculate wakes up says oh is there R new row context around yeah there is one on the product table and says oh I hate Ro context I'm going to destroy that but I'm a fair function so I will replace the row context with an equivalent filter context and then I apply it so when the filter context is there working some will start and say oh how many row do I see in the current filter context and it is only one because calculate removed all the other rows from all the other because it transformed the row context into a filter context make sense now let's do the same with the other table sum of sales quantity or whatever any column from the sales table what number will appear louder total quantity for that product or total quantity for all the products all the products because there is no filter context working and so this is showing the sum of all the products trust me this is the sum of quantity for all the products doesn't really matter the number now let's try with ah calculate what is going to happen in now of all the sales for produ because there is a row context the row context will be transformed into a filter context by context transition happening because of calculator and now it's a filter context and the filter context says oh is there any relationship around yeah there is one going from the product to the sales table I'm jumping on in I'm reaching the sales table in order to show only the sales of the given product and so what you get as a result is a different number which are the quantity sold for that product only okay that's an interesting question now you use related when you already have a row context what is what is totally useless is related table in reality related table is equivalent to calculate and then the table because they they do the very same so calculate calculate table and related table are synonym you can use one instead of the other but the feeling was that giving a name like related table made it simpler at the beginning to understand what is happening but in reality reality table is totally useless as a function it's just a nice name now the last step is this one I take some of sales quantity and I I wish I had the right click button but I don't so I copy on the clipboard and then I create a new measure that I call sum of sales quantity so sum of sales quantity is a measure that contains sum of sales quantity there is no calculate anywhere sum of sales quantity that's it now that I have the measure in test I remove everything and I replace it with a c to the measure sum of sales quantity what number is it going to appear based on what you know so far remember that there is no calculate anywhere yeah but I'm waiting for people who do not know that go ahead what number is going to have to appear there theoretically you should see the total of all the SES because there is no calculate anywhere but unfortunately what you see are only the sales of the current product and the reason is that whenever you call a measure from inside any piece of Dax code there is an automatic calculate which is added by the engine just because it's a measure so it's a measure I will not calculate and now that you have seen how the presence of calculate changes the semantics of your code so if calculate is there you get a number if you remove calculate you get a totally different number so you know how important it is to know whether calculate appears somewhere in your code and because it's automatically created when automatically added whenever you invoke a measure that's the reason why I typically tell everybody whenever you reference a column put the table name in front of the column and whenever you reference a measure like in this case never ever put the table name in front of the measure because when I look at this code in this way I read it as oh sum of sales quantity is a measure why that I here in powerbi I because it's powerport but when you just look at code you see it just because it doesn't have the table name in front of it if it was a column then it would had it would I would have put there the the table name just to make sure that everybody understand that this is a column there's no calculate there so you can read it as a column if it was a measure I would have removed the table name trust me this is extremely important up to the point that I typically refuse to the back code if it doesn't follow this rule because I see 30 or 50 lines of Dax Cod and they are impossible to read if you don't have a clear understanding of what is a measure and what is a column now let's go back to the slides so whenever you write something like some X over orders of sales amount there is no table name here so I read it as some xover orders of calcul calate sales amount okay just because it's a measure if sales amount is a measure if sales amount is not a measure that is not a problem I would have written sales sales amount okay how much time do I have oh I have half an hour that's more than enough well not more than enough but I will decide what to do as an example let's try to WR write together a simple measure that computes the daily average of sales so I have a sales amount and I want to compute the average of sales per day make sense let's go back to the demo machine and let's do that together just send me ideas I let me remove some stuff like the number let's me remove everything so we can put the calendar here uh let's get rid of the data so what I want to see here I want to see the sales amount which is here and then I want to see another column that shows the average sold per day how would you compute it a distinct count of the calendar table for days and then taking total sales divided by that number you can do better than that the idea is not bad but you can do better I mean for theage price on that day no for the average amount sold per day so in a day will be the same number but over a month will not be the sum of everything but will be the average might be what people typically answer I'm glad that here everything is slightly different is that do something like average over sales uh sales amount where is it so Computing the average over sales of sales amount as average so per day then again I have no idea where I put it in the customer table beautiful the number that you see here is not the average so per day but it's the average amount per row it's not per day okay another way of writing it is say oh you need to do that by day so use an average X you remember it ators yeah yeah know it's wrong and you do for every day so you iterate over the date and then you do as SMX over sales of sales quantity multiplied by sales net price or unit price that ISS this is iterating over date and then for each date it Computing the sales quantity multiplied by the sales net price and you get some number but you can intuitively see that it doesn't make much sense what's wrong here or what is the reason why this number is not the correct value louder I didn't hear sorry date is not the row cont that row date is in the row context I probably need to zoom I'm iterating over the DAT data is the daily level so I'm doing that row by row and for each row I'm iterating over sales and Computing quantity multipli by Price the filter context what what is the imagine that you are here in February 2007 what what number is appearing there how is it computed unfortunately I I wish I we had much more time because this is one of the examples that I love to do to work more but before we move further I want to give you probably the most important point the most important detail which is how to debug and correct any Dux measure so it's it's really a golden rule which is harder to use than you will than you expect and it's a two-step rule if the measure that you see does not return the value that you want you need to follow these two steps the first first one is the hardest one and it is stop thinking whatever you are doing really stop doing that totally clear your mind and then follow the measure in a very sheepy way without thinking too much and asking yourself at every Point what is the current filter context so you will get an understanding of what is being computed in each part of the formula so the first part is stop thinking at everything it's not an of an average it's not it's Computing or it's doing no forget about everything and say okay the cell is the one where the mouse is pointing February 2007 what is the filter context at that point February 2007 two columns one is 2007 the other one is February and that is the filter context then we do an average over the date how many rows are visible in the date table 28 rows okay 2007 yeah it's not a leap year so there are 28 rows in February and so I'm iterating 28 times cool then for 28 times I compute the sumx over sales how many rows do I see in the sales table some X is iterating so how many iteration will it do louder 28 28 is the iteration of average X over dat then inside there is another iteration by SX and SX is iterating over sales sings those 2 every single sale that falls within all of the 28 days so for 28 times it's summing the sales of all February whatever number it is the point is that even if some X is executed inside average X samx sees all the sales that are visible in the current filter context and nobody changed the filter context so we are iterating by data but since we are not changing the filter context for 28 times we are Computing the sales of all February and of course the number makes no sense at all what we need to do is say oh I want to iterate 28 times and at during each iteration I want to see only the sales of that given day so I have a row context on the data and I want to transform it into a filter context on the sales table and you know who is able to do that you simply you put calculate here and now the number makes much much more sense it's not perfect I mean you probably need to debug it a bit and it's not yet perfect but at least it makes a bit more sense the problem is that it takes some time to understand and make everything all of this stuff work at the beginning it's not easy I remember when I was learning Dax I had formulas that did not work and of course there was nobody teaching it so I had no idea how to understand what was happening so I look at the formulate comput some value with no meaning at all I sent it here to Redmond to the product team saying the formula is not Computing what I want what's wrong and after a few minutes the formula came back with calculate somewhere and that formula was perfect and working and I was looking at it say well I I don't know what is happening here now at least we have a theory so we know what is happening the point is that the theory is not so easy at the beginning it's pretty simple to get fooled by uh all this stuff then after a while it becomes uh simpler let's go back to the slides please actuallya what was the what was the value number the formula we had a few seconds ago the average so per day oh the one we that calculate it was Computing for the average for 28 times it was computed the full sales so it's basically computed the full sales divided by 28 no times 28 inside some X but then it it was doing an average X over data so the average X was Computing 28 times the same number which was 28 times and getting mad we can write it down with Excel and get some meaning now I mean we can uh we can try to understand exactly what number was that but it's more an exercise than that but the the main point here the the main reason why I typically show these kind of examples is because what I want people to understand I don't don't expect everybody here to be a master of evaluation context after just a few hours that you see the code my main concern is making you understand that if you know the theory well then you can look at the number and understand what is what it is being computed if you don't it might take time but you can do that if you just trust a generic understanding of uh uh the language without knowing the theory you look at the number it returns something which is totally crazy and you have no idea of why that number is wrong the same happens also for right numbers you have a number it's correct but you have no idea whether that number will be correct in all the measure and all reports that your user will do so understanding the theory is mandatory if you want to uh to really trust your reports and understand what was happening you know evaluate form on the road any sorry is there an evaluate formul or evalate me you know what do you mean by evaluate like in Excel you evaluate form button you can step through the function no I don't think they're are going to do anything I don't know whether uh they are planning to implement something like it uh it would be nice to have it yeah because that way it would be extremely Dynamic keep in mind that there is always a fight between Excel people and the engine people basically the engine people want to keep everything extremely fast whereas the Excel people want to keep everything everything extremely flexible so let me do whatever I want the goals of the two kind of people are very different the engine need to work with tables with tens of billions of rows on Multi terabyte machines H while Excel users typically work with smaller models so for them speed is not an issue because everything is fast when you are with small models but when you reach the billions things start to be very different and then you need to worry about performance functions like evaluate or dirty things like those are killer for performance okay we have iterators there are a lot of iterators Max X mix X average X product X concatenate X standard deviation personen geometric mean there are really a lot of them and nearly all of them follow the same pattern if you want to compute the average the minimum or the percentile over a table you simp typically iterate it use calculate to consolidate the row context into a filter context compute the value and then you get the value that you want now I have only a few minutes left I don't have many slides but there's no way I I need I would need to run too fast to go through the the end so I just want to show you the the basic introduction to time intelligence just to have an idea of how time intelligence works because the point the good point about DX is that once you learn evaluation context row context filter context and all this stuff you are done there is nothing else that you need to learn everything else is just an application and H some variation on the same theme so alltime intelligence is extremely simple to implement I I guess you know what time intelligence is say yes year to date month to date quarter to date running total all these kind of calculations are what we typically refer to as time intelligence and in order to make them work you need two things well either you simply trust the new features which is coming in powerbi of automatic time intelligence which I don't I prefer to write The Code by myself so I know what is happening otherwise if you want to do time intelligence by yourself then you need to uh write The Code by yourself let's try for example to write a year to date a very simple year to date to see how time intelligence works first of all you need to have a calendar table so if you want time intelligence to work you need to have a table in the model that contains the calendar that calendar need to have a few characteristics it need to contain all the days of the year so doesn't matter if you start selling in June the 1st of January of that year need to be present and all the days need to be there no holes at all even for vacation or whatever no 365 rows per each day and that table need to have a column inside which is a daytime that's all it's needed to build time intelligence if you don't have have such a table you build it by yourself using Excel using SQL or whatever you prefer but you need such a table and then to build to compute year to dat let's start with something uh we can do everything on the demo machine and then we will see the slides maybe later let's get rid of average so per day and um okay now this is not what I want to see I want to see Let Me Show in a matrix I want to see the calendar 2007 January and then individual dates and I have the sales here imagine that I want want to compute a year to date if you want if I want to compute a value here for the 18 of January 2007 the number that will appear here is the sum of all the numbers that appear before okay if you think in Excel way that is the previous number plus the current one but you cannot do anything like that in Dax what you can do is saying oh I have a filter context that contains only the 18 of January and I want to place it with another filter context that contains all the dates in the same year that happen to be before the F the 18 of January and replacing the filter context so creating a filter context that contains all the dates of the Year up to the current date I will be able to compute the value I want make sense since it's a too complex exercise we can write it one step at a time so we can create a new measure let's call it sales a year to date uh we need we start with calculate yeah I know that the measure we want to compute is always sales amount sales amount will compute the measure and let's start with something simple like a filter instead of computing the year today let's compute the sales between two dates and we Define the dates in a fixed way so we develop it one step at a time so we filter the date where the date data is greater or equal where is is greater or equal date 2007 I guess 2007 011 and at the same time the date is less than 20071118 okay so this is Computing oh okay this is Computing a fixed value and it will be the same value for all the rows make sense let's see what happens if we add it to the model doesn't seem to be correct because it's showing values for the first seven days of the year and then nothing else in yeah that is fine if I use 18 here it will show value for the first 18 months but it's the same value it's not a year to date the problem is that I'm filtering date that's not what I want to do what I want to do is go out of the current filter context filter all the dates and then return me the two boundaries now it makes sense and it shows the values also for 2005 that is fine because for any date it always shows the sum of well there are just too many of them trust me this is the sum of the first 18 or 17 days of 2007 so as a technique it works the idea is not bad the point is that what I I don't want these two values to be fixed I want these two values to be dynamic and change depending on what is happening in my report okay so I don't want I need to replace the upper and the lower boundary which something which is dynamic to do that let me remove this measure for one second let's create a new measure and we call it Max date which is equal to Max of dat date and then if you put Max data in the P table you see that for each day it shows the current day but then at the month level it shows the end of the mon because it's always the maximum date so in our original measure sales here to date we can replace these with Max of date date and now if I put sales year to n if I put sales year to date here if I put sales here to date here okay I see a number which has the maximum the limit on the top which is corrected unfortunately I don't have time to go through all the details but the point is if you for the minimum boundary you basically create a datea which is a data that has the year of the maximum of the date and then one one so this is returning all the dates between the 1 of January of the current year and the last visible date in the context and once you once you compute the measure in this way now sales year to date shows for the 1st of January the same value then a different value which is the sum of these two then a different value maybe zooming it would help and then this number goes on growing growing growing always adding values to the previous role make sense so the point and unfortunately as I told you I'm already very very late so let's go to the slides and we go to the conclusion what I wanted to show you was not uh let's go bit through the slide please what I wanted to show you is that Computing something like something simple like a year to date if you understood evaluation context filter context and all this stuff is extremely simple whereas if you didn't understand evaluation context row context and filter context yeah you can trust Standard Time intelligence function and everything will work but you will not be able to do uh really complex calculations as I told you unfortunately I don't have time to run through all the demos about time intelligence and probably you are already tired enough after two hours of Dax evaluation context and stuff so it's not worth going into more details the point is if you need that if you are new to Dax remember that there is only one thing that you need to focus on which is evaluation context row context filter context and calculate spend your time there learn them the right way and then time intelligence and everything else is pretty simple to do if you are already using Dux since a while then that was just a reminder to play with evaluation context and this stuff I already said at the beginning if you're interested in all this stuff and a lot more about Dax probably too much for nearly everybody uh on that book you will find a lot of details about the engine the internals plus many more details about Strange Behaviors of calculate as I told you we have seen the introduction of calculator for very complex measures or very complex scenario the scenario is a bit more complex and a bit more advanced so it requires more time now I think I'm extremely late so thank you for attending everybody and uh