I have 2 tables, AvailabilitiesDB and AvailabilityTemplateDB.
The AvailabilitiesDB table looks like like
IsAvailable StartTime FinishTime Employee_ID Week Day Of the Week
1 25-09-2020 16:00:00 25-09-2020 19:00:00 101 39 5
0 27-08-2020 14:00:00 27-08-2020 17:00:00 13 35 4
1 25-11-2020 09:00:00 25-11-2020 18:00:00 66 48 3
The AvailabilityTemplateDB table looks like this:
Day Of The Week StartTime FinishTime Employee_ID
3 18:00:00 21:00:00 101
2 11:00:00 17:00:00 13
6 06:00:00 20:00:00 66
Here is the issue:
I want to calculate the availability of the employees, meaning their minutes available (so from StartTime to FinishTime).
However, the thing is: The employees all have a template (AvailabilityTemplateDB) which is their go-to work schedule, so if there is not any observations in AvailabilitiesDB, it means that they use their template. But if there is an observation for a specific employee in the AvailabilitiesDB, it means that the template has been overwritten for the specific day. The templates are recurrent every week, which is why they don't have specific dates, and just "Day of the Week".
So I want to calculate something along the lines of: calculate DatesBetween (Minutes), where employee id = employee id, and if there is a data in the availabilitiesDB it must use this number, so for the first observation it would be 180 minutes, but if there is not a date in there, it must take the time from the template table. I find this hard because all I have is "Day of the week" in the template table, and I don't know how i would "convert" it to dates.
This was what I have tried (Not near end result) and obviously it's not supposed to count rows, I was just trying in order to come closer to an answer. Date refers to my date table, which holds unique dates for all of the dates from the AvailabiltiesDB table.
Try =
VAR dates =
DATESBETWEEN (
'Date'[Date].[Date],
MIN ( 'Date'[Date].[Date] ),
MAX ( 'Date'[Date].[Date] )
)
VAR Employee_ID = AvailabilitiesDB[Employee_ID]
VAR Weekdays = AvailabilitiesDB[Day Of the Week]
VAR weekdaysAndDateMatch =
VAR COUNTZ =
CALCULATE (
COUNTROWS ( AvailabilitiesDB ),
FILTER (
AvailabilityTemplateDB,
Weekdays = AvailabilityTemplateDB[Day of the week]
&& Employee_ID = AvailabilityTemplateDB[Employee_ID]))
RETURN IF ( COUNTZ = 0, "Yes", "No" )
My desired outcome would be a column in the AvailabilitiesDB table that summed the availability in minutes per week, for every employee_ID.
I hope you can help me further, thank you in advance.
Events with a duration are simplified by implementing a snapshot table.
Another good practice when dealing with time is to split the date part and the time part in two different columns
So I created a small model with dimension Date, Employees and Time
For this example we just need the months from August to November. To match the Day of the week with the sample data I had to subtract 1.
Date =
ADDCOLUMNS (
CALENDAR ( "2020-08-01", "2020-11-30" ),
"WeekDay", WEEKDAY ( [Date] ) - 1
)
The Employees dimension is
Employees =
ADDCOLUMNS (
DISTINCT ( AvailabilitiesTemplateDB[Employee_ID] ),
"Name", FORMAT ( [Employee_ID], "000" )
)
For the time dimension I created a measure as a parameter with the time granularity to be used also for the snapshot table. This can be changed to adapt the model to the requirements.
To keep a low number of lines for this example I used a 1 hour interval
TimeInterval = VALUE( "01:00:00" )
So the Time table becomes
Time =
SELECTCOLUMNS (
GENERATESERIES ( VALUE ( "00:00:00" ), VALUE ( "23:59:59" ), [TimeInterval] ),
"Time", [Value]
)
The snapshot table is a table containing a row per each time interval for which an employee is available. To build this table we can use the set functions UNION and EXCEPT. The snapshot table has just three columns: the Employee_ID, the Date and the Time
This code matches the Date with the weekday using the Date table we build before
AvailabilitiesSnapshot =
VAR TimeGranularity = [TimeInterval]
VAR AvailableFromTemplateDB =
SELECTCOLUMNS(
GENERATE(
AvailabilitiesTemplateDB,
VAR DayOfWeek = AvailabilitiesTemplateDB[Day Of The Week]
VAR TimesTable =
SELECTCOLUMNS(
GENERATESERIES(
AvailabilitiesTemplateDB[StartTime],
AvailabilitiesTemplateDB[FinishTime] - TimeGranularity,
TimeGranularity
),
"Time", [Value]
)
RETURN
GENERATE(
FILTER( ALLNOBLANKROW( 'Date' ), 'Date'[WeekDay] = DayOfWeek ),
TimesTable
)
),
"Employee_ID", [Employee_ID],
"Date", [Date],
"Time", [Time]
)
VAR AvailableFromDB =
SELECTCOLUMNS(
GENERATE(
CALCULATETABLE( AvailabilitiesDB, AvailabilitiesDB[IsAvailable] = 1 ),
VAR TimesTable =
SELECTCOLUMNS(
GENERATESERIES(
AvailabilitiesDB[StartTimeOnly],
AvailabilitiesDB[FinishTimeOnly] - TimeGranularity,
TimeGranularity
),
"Time", [Value]
)
RETURN
TimesTable
),
"Employee_ID", [Employee_ID],
"Date", [Date],
"Time", [Time]
)
VAR UnavailableFromDB =
SELECTCOLUMNS(
GENERATE(
CALCULATETABLE( AvailabilitiesDB, AvailabilitiesDB[IsAvailable] = 0 ),
VAR TimesTable =
SELECTCOLUMNS(
GENERATESERIES(
AvailabilitiesDB[StartTimeOnly],
AvailabilitiesDB[FinishTimeOnly] - TimeGranularity,
TimeGranularity
),
"Time", [Value]
)
RETURN
TimesTable
),
"Employee_ID", [Employee_ID],
"Date", [Date],
"Time", [Time]
)
RETURN
EXCEPT(
DISTINCT( UNION( AvailableFromTemplateDB, AvailableFromDB ) ),
UnavailableFromDB
)
In this code first we add all available intervals, then we remove the intervals that are marked as not available, those with AvailabilitiesDB[IsAvailable] = 0
Then we create the relationships between this new snapshot table and the dimensions, to get the model
with this model, writing a measure to compute the available minutes is straigthforward
AvailableTime = COUNTROWS( AvailabilitiesSnapshot ) * [TimeInterval] * 24 * 60
The TimeInterval parameter can be changed to rise the detail level. Of course this will increase the number of rows of the snapshot table and also depends on the size of the input tables.
Related
I have a measure that totals the values for each date in the table. I want to filter this measure so that I can display only the last 28 days present in the table instead of displaying values for all the dates. Following is the code that works for getting totals for full table:
CALCULATE( SUM(Daily_Reports[Confirmed]),
FILTER( ALL(Daily_Reports),
Daily_Reports[Case_Date] = SELECTEDVALUE(Daily_Reports[Case_Date]) ) )
The 'relative date' filter in the Filters pane does not work because it only accepts the last 28 days based on today's date and not the dates in the table. Please suggest a DAX formula that can filter for the last 28 days present in the table.
Try this code
VAR endDay = LastDate(Daily_Reports[Case_Date])
VAR startDay= DATEADD(endDay,-28,DAY)
VAR setOfDates = DATESBETWEEN(Daily_Reports[Case_Date], StartDate, EndDate )
RETURN
CALCULATE(
SUM(Daily_Reports[Confirmed])
,setOfDates
)
You can try this one:
MS =
CALCULATE (
SUM ( Daily_Reports[Confirmed] ),
FILTER (
ALL ( Daily_Reports[Case_Date] ),
Daily_Reports[Case_Date]
>= SELECTEDVALUE ( Daily_Reports[Case_Date] ) - 28
)
)
This is what finally worked for me. I created a measure (not a column), that returns 1 for the last 28 days with an IF clause, leaving it blank if the date is not in the last 28 days as follows:
Last28 =
VAR MaxDate = LASTDATE( ALL(Daily_Reports[Case_Date]) )
VAR MinDate = DATEADD( MaxDate, -28, DAY )
RETURN
IF( SELECTEDVALUE(Daily_Reports[Case_Date]) >= MinDate && SELECTEDVALUE(Daily_Reports[Case_Date]) <= MaxDate, 1 )
Then I incorporated this measure into the Calculate function as follows:
Daily Cases =
CALCULATE( SUM(Daily_Reports[Confirmed]),
FILTER( ALL(Daily_Reports),
Daily_Reports[Case_Date] = SELECTEDVALUE(Daily_Reports[Case_Date]) && NOT(ISBLANK(Daily_Reports[Last28]))
)
)
I have the following fields:
Year
Category
Maker
Month
Month Number
Sales Volume
Sales
Date
So, I have in my dash a filter for "Month Number" and "Year":
My goal is to create two new measure; first with the Rolling Year that need to sum 12 months, ending in the moment that the user select in the mencioned filters. For example if y select Year 2021 and Month 01. The Rolling Year need to sum the sales of a selected category since 2020-02 to 2021-01 (thats mean always 12 months since a pivot month).
For thesecond is exactly the same, a measure called Rolling Last Year, it need to be a rolling sum too, but for the last period in order to compare. Taking the same example if I have the period 2020-02 to 2021-01. The Rolling Last Year for the last period should be 2019-02 to 2020-01.
I tried with this DAX code, that extracted from Microsoft page but without success:
Rolling Year =
CALCULATE (
SUMX ( Table, Table[Sales] ),
FILTER (
ALL (Table[Date] ),
AND (
Table[Date] <= MAX ( Table[Date] ),
DATEADD ( Table[Date], 12, MONTH ) > MAX ( Table[Date] ))))
I share you an extract of my base:
Thanks in advance !!!
Based on the table and code you have shared, it is unclear from where the date filters are being applied. In case you have not done it, I strongly suggest to delete the [Month] and [Month Number] field from your Sales table and keep them in a separate Calendar table, from where the filters should be selected. Then, a simple tweak on you current formula should do the trick:
Rolling Year =
CALCULATE (
SUMX ( Table, Table[Sales] ),
FILTER (
ALL ('Calendar'[Date] ),
AND (
Table[Date] <= MAX ( 'Calendar'[Date] ),
DATEADD ( 'Calendar'[Date], 12, MONTH ) > MAX ( 'Calendar'[Date] ))))
However you can try with this variation for the code, a little bit optimized so as not to scan your whole Sales table each time:
Rolling Year =
VAR EndSelectedDate = MAX ( 'Calendar'[Date] )
VAR StartSelectedDate =
CALCULATE (
MAX ( 'Calendar'[Date] ),
ALL ( 'Calendar'[Year] ),
'Calendar'[Year]
= MAX ( 'Calendar'[Year] ) - 1
)
RETURN
CALCULATE (
SUM ( Table[Sales] ),
ALL ( 'Calendar' ),
'Calendar'[Date] <= EndSelectedDate,
'Calendar'[Date] > StartSelectedDate
)
have a simple table like this
ID value date
A 100 2020-01-01
B 80 2019-01-01
A 90 2022-01-01
A 130 2021-01-01
B 100 2021-01-01
and want to know how many IDs had 10% increase within a selected time period, IDs might not necessarily have an entry on Min(Date) or Max(Date), I'd like to know the difference between first and last value within the selected time period
I came up with something like this but not sure if it's working :
Measure=
Var FirstValue=
SUMX (
VALUES( table [ID] ),
CALCULATE ( MIN ( table[value]), FIRSTDATE ( 'Date'[DATE] ) )
)
Var LastValue =
SUMX (
VALUES( table[ID] ),
CALCULATE ( MIN ( table[value] ), LASTDATE ( 'Date'[DATE]) )
)
Return sumx(table,if( (FirstValue -LastValue )/FirstValue> 0.1 , 1,0)
)
I have the following values in Table_1:
date sales_amount
04/01/2021 100.00
04/02/2021 300.00
04/05/2021 500.00
I want to compute a running average, so the average is computed as each day passes, so that my final output looks like this:
date sales_amount running_average
04/01/2021 100.00 100.00
04/02/2021 300.00 200.00
04/05/2021 500.00 300.00
The sales person did not work on 04/03 and 04/04, so I want to exclude them from my running average.
Right now, my output looks like this, which is wrong for what I am doing:
date sales_amount running_average
04/01/2021 100.00 100.00
04/02/2021 300.00 200.00
04/05/2021 500.00 180.00
Any suggestions?
Right now, my DAX code looks like this:
test_new =
VAR LastVisibleDate = MAX('Table_1'[Date])
VAR FirstVisibleDate = MIN('Table_1'[Date])
VAR LastDateWithSales =
CALCULATE(
MAX('Table_1'[Date]),
REMOVEFILTERS()
)
VAR Result =
IF (
FirstVisibleDate <= LastDateWithSales,
CALCULATE(
AVERAGE([Sales_Amount]),
Table_1[Date]
)
)
RETURN
Result
I have added a few lines in the last variable:
Table_1[Date] in the last variable basically means FILTER ( ALL ( Table_1[Date] ), TRUE ), and that will return all of the dates so not useful in your scenario as it will give wrong results, instead write Table_1[Date] <= LastVisibleDate
You don't need REMOVEFILTERS when you have Date column coming from the Dates table and the Date table is marked as a Date table because when a date table is marked as a date table the engine automatically adds a REMOVEFILTERS/ALL whenever a filter is applied over the Date column, but that won't happen with Date column of other tables hence you need to write an explicit REMOVEFILTERS in the last variable
test_new =
VAR LastVisibleDate =
MAX ( 'Table_1'[Date] )
VAR FirstVisibleDate =
MIN ( 'Table_1'[Date] )
VAR LastDateWithSales =
CALCULATE (
MAX ( 'Table_1'[Date] ),
REMOVEFILTERS ()
)
VAR Result =
IF (
FirstVisibleDate <= LastDateWithSales,
CALCULATE (
AVERAGE ( [Sales_Amount] ),
Table_1[Date] <= LastVisibleDate,
REMOVEFILTERS ( Table_1 )
)
)
RETURN
Result
As a best practice always use Date column from a Date table.
I have a scenario with two data slicers. The first data slicer filters data for one period, the second one for another period. By editing visual interactions I got this works at the same page.
Now I want to compare two resulting values (in this case, number of transactions, and find a percentage of change between two selected periods.
I duplicated data column so I have two date columns for each slicer and I calculated the next measures:
# of Transactions 1 = CALCULATE(COUNT(Report[ProductID]),DATESBETWEEN(Report[Date1],[Start Date 1],[Last Date 1]))
# of Transactions 2 = CALCULATE(COUNT(Report[ProductID]),DATESBETWEEN(Report[Date2],[Start Date 2],[Last Date 2]))
% Transaction Change = ([# of Transactions 1]/[# of Transactions 2]) - 1
The first 2 measures are accurate (# of Transactions 1 & 2), but % of change doesn't work.
If you look at the screenshot below, you'll see # od Transactions 1 = 1,990 and # of Transactions 2 = 2,787. I want to compare this 2 values now.
How can I solve this?
Thank you.
First create two measure for your date bounds:
Min Date :=
MIN ( 'Report'[Date] )
Max Date :=
MAX ( 'Report'[Date] )
Then create a date table using the following DAX, this will join to you 'Report' table on the primary date:
Dates :=
VAR MinDate = [Min Date]
VAR MaxDate = [Max Date]
VAR BaseCalendar =
CALENDAR ( MinDate, MaxDate )
RETURN
GENERATE (
BaseCalendar,
VAR BaseDate = [Date]
VAR YearDate =
YEAR ( BaseDate )
VAR MonthNumber =
MONTH ( BaseDate )
VAR YrMonth =
100 * YEAR ( BaseDate )
+ MONTH ( BaseDate )
VAR Qtr =
CONCATENATE ( "Q", CEILING ( MONTH ( BaseDate ) / 3, 1 ) )
VAR YrMonthQtr =
100 * YEAR ( BaseDate )
+ MONTH ( BaseDate )
& CONCATENATE ( "Q", CEILING ( MONTH ( BaseDate ) / 3, 1 ) )
VAR YrMonthQtrDay =
100 * YEAR ( BaseDate )
+ MONTH ( BaseDate )
& CONCATENATE ( "Q", CEILING ( MONTH ( BaseDate ) / 3, 1 ) )
& DAY ( BaseDate )
RETURN
ROW (
"Day", BaseDate,
"Year", YearDate,
"Month Number", MonthNumber,
"Month", FORMAT ( BaseDate, "mmmm" ),
"Year Month", FORMAT ( BaseDate, "mmm yy" ),
"YrMonth", YrMonth,
"Qtr", Qtr,
"YrMonthQtr", YrMonthQtr,
"YrMonthQtrday", YrMonthQtrDay
)
)
Now create another date table from which to compare, and join to your primary date table in 'Report' and ensure the relationship is inactive:
Compare Dates :=
ALLNOBLANKROW ( 'Dates' )
Now create the [# of transaction] measure; one for 'Dates' and another for 'Compare Dates' like so:
[# of Transaction 1] :=
CALCULATE (
COUNT ( Report[ProductID] )
)
[# of Transaction 2] :=
CALCULATE (
[# of transaction 1],
ALL ( 'Dates' ),
USERELATIONSHIP ( 'Compare Dates'[Date], 'Report'[Date] )
)
Now Create the % Delta measure:
Transaction Change := CALCULATE(DIVIDE([# of Transactions 1],[# of Transactions 2]) - 1)
This should work like a charm and will work for any dates selected in your slicers, you will still need to associate your date slicers with your new date tables.
I hope this helps!!