I have two tables (Sales,Stock) both have a DateKey and ProductCode. The requirement is to count the Distinct ProductCodes across both tables by Financial week driven by a custom Date Dimension. There is a further requirement to take the Average of the Weekly Distinct counts if multiple weeks are selected not the distinct count for all weeks.
The below DAX works in my unit tests, however on real data volumes (300+ million rows imported) it grinds to a complete standstill. If I select a week it takes about 5 sec, for 12 months its 60 seconds. They need to do YoY and WoW calcs so this perfomance will not be good enough. Not sure how else to achieve this.
Here is the DAX I've written:
Option Count =
// Get options at a week level for Sales
VAR SalesOptions =
SELECTCOLUMNS (
Sales,
"Week",
CALCULATE (
MAX ( Dates[Financial Year Week Number] ),
FILTER ( ALL ( Dates ), Sales[DimBilledDateSKey] = Dates[DimDateSKey] )
),
"ProductOptionCode", 'Sales'[ProductOptionCode]
)
// Get options at a week level for Stock
VAR StockOptions =
SELECTCOLUMNS (
Stock,
"Week",
CALCULATE (
MAX ( Dates[Financial Year Week Number] ),
FILTER ( ALL ( Dates ), Stock[DimDateSKey] = Dates[DimDateSKey] )
),
"ProductOptionCode", 'Stock'[ProductOptionCode]
)
// Union the options from both and then apply a distinct so that a count of the product code would be the same result as a distinct count
VAR CombinedOptions = DISTINCT ( UNION ( SalesOptions, StockOptions ) )
RETURN
// Average the weekly disitnct option count
AVERAGEX(
// Group the above union by week and do a count of the options for the week. They should now be distinct due to the distinct applied to the union
GROUPBY(CombinedOptions, [Week], "Week Option Count", COUNTX(CURRENTGROUP(), [ProductOptionCode] )),
[Week Option Count]
)
I built a small model with a fake Dates dimension and Stock and Sales fake tables
Dates =
SELECTCOLUMNS(
CALENDAR(
"2020-01-05",
"2020-12-26"
),
"DimDateSKey", [Date],
"Financial Year", YEAR( [Date] ),
"Financial Year Week Number",
INT( ( [Date] - "2020-01-05" ) / 7 ) + 2
)
Sales = DATATABLE(
"DimBilledDateSKey", DATETIME,
"ProductOptionCode", INTEGER,
{
{"2020-01-05", 1},
{"2020-01-06", 1},
{"2020-01-06", 2},
{"2020-01-12", 1}
}
)
Stock = DATATABLE(
"DimDateSKey", DATETIME,
"ProductOptionCode", INTEGER,
{
{"2020-01-09", 1},
{"2020-01-06", 3},
{"2020-01-08", 3},
{"2020-01-13", 2}
}
)
With the two relationship over the dates it's easy to filter for the fiscal week, this avoids the need to iterate over the full Date table.
To calculate the average number of the distinct product codes it's enough to iterate over the selected Financial Year Week Number and then compute the COUNTROWS of the DISTINCT of the UNION of the Sales[ProductOptionCode] and the Stock[ProductOptionCode] for the currently iterated week
Option Count =
AVERAGEX (
VALUES( Dates[Financial Year Week Number] ),
CALCULATE (
COUNTROWS (
DISTINCT(
UNION (
VALUES( Sales[ProductOptionCode] ),
VALUES( Stock[ProductOptionCode] )
)
)
)
)
)
If the real model doesn't contain the relationship with the DimBilledDateSKey, maybe because there already is another relationship with another dateKey in the Sales table, it's always possible to add an inactive relationship to be activated with USERLATIONSHIP inside the CALCULATE.
It should be faster, since it uses relationships, that are usually the best option from the performance point of view and minimizes the usage of Set functions, that are expensive, since they materialize the tables in memory.
Related
I need to write a DAX measure that calculates (e.g., "Count Rows"), but only when another measure value is evaluated (e.g., filtering "[Sales]>100"). So if-- in the context of the selected filters-- Sales is great than 100, then the measure is executed only for those rows.
The measure I have defined works in the context of lower smaller grain. But the totals do not sum correctly.
Any suggestions?
DAX Measure
License Usage =
// Users with active viewership in 3 months
IF (
NOT ( ISBLANK (
CALCULATE (
[Activity Date NEWEST],
KEEPFILTERS ( DATESINPERIOD ( dimCalendar[Date], TODAY (), -90, DAY ) )
)
)), 1
)
Activity Date NEWEST =
MAX('PBI Activity'[Date])
Okay, I figured something out that works.
DAX
License Usage =
// Users with active viewership in 3 months
CALCULATE (
[Count Users],
FILTER ( 'PBI Activity', 'PBI Activity'[Date] >= TODAY () - 90 )
)
Count Users = COUNTROWS('Users')
Also, I later came across this article which looks like it also does what I was hoping to do: Execute calculate expression over filtered rows based upon measure filter.
Reference: Specifying multiple filter conditions in CALCULATE - SQLBI
DAX
DEFINE
MEASURE Sales[Big Sales Amount] =
CALCULATE (
[Sales Amount],
KEEPFILTERS (
FILTER (
ALL ( Sales[Quantity], Sales[Net Price] ),
Sales[Quantity] * Sales[Net Price] > 1000
)
)
)
EVALUATE
SUMMARIZECOLUMNS (
Sales[Quantity],
"Sales Amount", [Sales Amount],
"Big Sales Amount", [Big Sales Amount]
)
So, I have the following tables in my Power BI :
Sales : Date | ID_Client | ID_Product | Amount
Client : ID_Client | Name_Client
I would like to get the number of unique BIG clients in any given month. I therefore use the following formula (which I then put in a column in a table with months in rows):
# BIG Clients =
VAR threshold = 10000
RETURN
(
CALCULATE(
DISTINCTCOUNT( Sales[ID_Client] ),
FILTER(
SUMMARIZE(
Sales,
Sales[ID_Client],
"Sales", SUM( Sales[Amount] )
),
[Sales] >= threshold
)
)
)
QUESTION IS : how can I get the list of those BIG clients for any given month? Let's say I click on the November number of big clients in my table, could another table nearby display the list of those clients ?
Thanks in advance for your kind help, I've been trying for a while :)
I assume that you have a table of clients with the Name column with a one to many relationship with the Sales table and that you do not have duplicate client names. Then you may create a [BIG Sales] measure to be used in a table or matrix visual with client names on the rows.
since [BIG Sales] evaluates to BLANK() for clients with less that threshold sales, they are automatically filtered out from the visual
BIG Sales =
VAR threshold = 10000
VAR BigCustomers =
FILTER(
ADDCOLUMNS(
VALUES( Clients[Name] ),
"Sales", SUM( Sales[Amount] )
),
[Sales] >= threshold
)
RETURN
SUMX(
BigCustomers,
[Sales]
)
You could create a table or matrix visual with the client on the rows and use your measure in the values field. This will show 1 for all big clients and return blank for the rest (which should hide them). If you don't want to show the measure, you can set the value is 1 in the filter pane and remove the measure from the values field.
A more direct option is to use a simple SalesAmount = SUM ( Sales[Amount] ) measure in the values field and filter like this
Hi I have been struggling with this for a bit now and I hope I didn't miss a previous question. I am trying to get a count of the vehicles we have based on an EOMONTH date. We are buying and selling cars on a regular basis and for reporting we need to know how many we had at the end of each month and the report is a rolling 12 months.
I've tried creating the relationship with the purchasedate of the vehicle to the date of my date table but when I create the measure (Used to calculate the number of vehicles purchased but haven't been sold):
SalesBlank = CALCULATE(
COUNT(Vehicles[MVANumber]),
FILTER(Vehicles, Vehicles[purchasedate] <= RELATED('Date'[EOMONTH]) && ISBLANK(Vehicles[saledate])))
I only get a count of vehicles purchased that month and don't have a sale date - I'm not surprised because my relationship with the date table is the purchase date.
How can I set up a measure to look at the date table and filter the vehicles table with this logic:
purchasedate <= date[EOMONTH] && ISBLANK(salesdate)
Any help would be greatly appreciated!!
Thanks,
Matt
Sample Data and Desired Results
Relationships
If I understand you correctly, you want to get a count of the vehicles on hand at the end of each month. That could be calculated by counting the vehicles with a purchase date less than or equal to the selected end of month and subtracting the count of vehicles with a sale date less than or equal to the selected end of month.
You can create an active relationship between Vehicle[PurchaseDate] and Date[Date]. Then create an inactive relationship based upon Vehicles[SaleDate] and Date[Date].
You could use a measure that is something like this:
Inventory Count =
VAR MaxDate =
MAX ( 'Date'[Date] )
VAR MinDate =
CALCULATE ( MIN ( 'Date'[Date] ), ALL ( 'Date' ) )
VAR Purch =
CALCULATE (
COUNT ( 'Vehicles'[VehicleID] ),
DATESBETWEEN ( 'Date'[Date], MinDate, MaxDate )
)
VAR Sales =
CALCULATE (
COUNT ( 'Vehicles'[VehicleID] ),
USERELATIONSHIP ( 'Date'[Date], Vehicles[Sale Date] ),
DATESBETWEEN ( 'Date'[Date], MinDate, MaxDate )
)
VAR MaxPurDt =
CALCULATE ( MAX ( 'Vehicles'[Purchase Date] ), ALL ( 'Vehicles' ) )
VAR MaxSlDt =
CALCULATE ( MAX ( 'Vehicles'[Sale Date] ), ALL ( 'Vehicles' ) )
RETURN
IF (
MIN ( 'Date'[Date] ) <= MaxPurDt
|| MIN ( 'Date'[Date] ) <= MaxSlDt,
Purch - Sales
)
This measure gets a cumulative count of purchases and a cumulative count of sales and then subtracts them. The IF check is to avoid propagation of cumulative totals beyond the maximum date in the Vehicle table.
I'm not sure how to interpret your showing of just 3 months in the desired results. This will produce the same answers as what you have, but without a filter applied to the table, it starts at 3/31/2016 (the date of the first sale).
Edit: There's probably a more efficient way along the lines you were thinking, but it is escaping me at the moment.
I have a table which contains a list of products scores by date:
From this table, I have to make a plot of the cumulative percentage of each quality by date.
At this moment I have the percentage of each class by day:
For that I used this measurement:
Measure =
CALCULATE (
SUM ( Table1[Percentage_By_Class] ),
FILTER ( Table1, Table1[Date] = MAX ( Table1[Date] ) ),
ALLEXCEPT ( Table1, Table1[Score] )
)
/ CALCULATE (
SUM ( Table1[Percentage_By_Class] ),
FILTER ( ALL ( Table1 ), Table1[Date] = MAX ( Table1[Date] ) )
)
But this only considers the percentage of each day. I need to consider all previous dates. E.G. for day 2 I need to consider days 1 and 2, for day 3 I need to consider days 1,2,3 and so on.
How can I accomplish this?
in my opinion, you need a calendar for the date first, you can create a table easily bay dax function =CALENDARAUTO() And mark it as a calendar table,
After that, you can use a DATEMTD or a DATEYTD function for your coding purpose.
here are the steps:
1 - https://learn.microsoft.com/en-us/dax/calendarauto-function-dax
select left pane --> table --> modelling / create table and add dax formula
2- reference the table as a date table, right click on the table from the right pane
after then you can use data functions like YTD MTD ,
new measure :
1st mesure : AVG1 = AVG(DATA_)
2nd measure : YTD AVG ALL = CALCULATE([AVG1];DATESYTD(CALENDAR[DATE]))
REF: https://learn.microsoft.com/en-us/dax/dateadd-function-dax
then you can use MONTH(CALENDAR(DATE)) on left and YTD AVG as a value at any table...
regards.
SUNAY
I try to calculate moving average in DAX power bi. I use different codes, for example this.
Moving AverageX 7B Days =
AVERAGEX (
DATESINPERIOD(
sahkoInput[Date];
LASTDATE ( sahkoInput[Date]);
-7;
DAY
);
sahkoInput[price]
)
All codes give the same result - Moving AverageX 7B Days is equal to column "price". What went wrong and how to fix it?
Firstly, I would look to add a date/calendar table to your data model.
This can be as simple as a list of consecutive dates from at least seven days before your first data point until after the end of when you expect your last one to be.
The reason for this is that the date functions in DAX always work best when they have a table of consecutive dates to look at - you can get unpredictable results when your fact table doesn't have any data on a particular date.
Once you have added the date table, create a relationship to link the date column in your sahkoInput table to the date column in your date table.
Now, the following measure should work:
Moving AverageX 7B Days =
CALCULATE (
AVERAGE('sahkoInput'[Price]);
DATESINPERIOD ('DateTable'[Date];
LASTDATE ('DateTable'[Date]);
-7;
DAY)
)
Create a date table, this will dramatically improve performance and increase readability. You can do that by using the following DAX:
Min Date := MIN('sahkoInput'[Date])
Max Date := MAX('sahkoInput'[Date])
Dates :=
VAR BaseCalendar =
CALENDAR ( [Min Date], [Max date] )
RETURN
GENERATE (
BaseCalendar,
VAR BaseDate = [Date]
VAR YearDate =
YEAR ( BaseDate )
VAR MonthNumber =
MONTH ( BaseDate )
RETURN
ROW (
"Day", BaseDate,
"Year", YearDate,
"Month Number", MonthNumber,
"Month", FORMAT ( BaseDate, "mmmm" ),
"Year Month", FORMAT ( BaseDate, "mmm yy" )
)
)
Then referencing this date table you can create an average using the standard average function, like so:
Moving Average 7 Days :=
CALCULATE (
AVERAGE ( 'sahkoInput'[Price] );
KEEPFILTERS ( DATESINPERIOD ( 'Dates'[Date]; MAX ( 'Dates'[Date] ); -7; DAY ) )
)
I hope this helps!