Need help converting date functions from SQL Server to DAX - powerbi

I am trying to convert the following date function from t-sql to dax. My goal is to get the adjusted_date column I got from SQL into PowerBi.
select
EOMONTH(DATEADD (dd, -1, DATEADD(qq, DATEDIFF(qq, 0, [date]) +1, 0)), 1)
;
So far I have DATEDIFF as
DATEDIFF( 0, [date], QUARTER)
however I can't figure out how to do DATEADD. For that I have this:
DATEADD(0, DATEDIFF( 0, [date], QUARTER)+1, quarter)
I got this function from https://dax.guide/dateadd/
I know EOMONTH exists in DAX as show here: https://learn.microsoft.com/en-us/dax/endofmonth-function-dax
I was hoping someone can provide my a hint or help me. Thank you.
date,adjusted_date
2020-01-01,2020-04-30
2020-02-05,2020-04-30
2020-03-22,2020-04-30
2020-04-01,2020-07-31
2020-05-28,2020-07-31
2020-06-19,2020-07-31
2020-07-01,2020-10-31
2020-08-01,2020-10-31
2020-09-17,2020-10-31
2020-10-01,2021-01-31
2020-11-09,2021-01-31
2020-12-20,2021-01-31

TLDR: DAX
ENDOFMONTH(DATEADD(ENDOFQUARTER([date]), 1, DAY))
While we can translate expressions between the two query languages, it is important to understand the original SQL expression and be able to explain it in terms other than SQL. This will help to avoid confusion but will also help you to identify alternative native functions that will produce the same result using syntax that is more intuitive and easier to maintain.
Readability of your code is always important, if the intent of your code is not obvious, then future maintenance tasks may misinterpret your logic and refactor it into something else entirely.
Your initial SQL is selecting the Last Day of the First Month in the Next Quarter, but it is getting there in a convoluted way, it is going to the last day of the quarter and then finding the last day of the next month.
SQL
EOMONTH(DATEADD (dd, -1, DATEADD(qq, DATEDIFF(qq, 0, [date]) +1, 0)), 1)
-- Simplified to:
EOMONTH(DATEADD(qq, DATEDIFF(qq, 0, [date]) +1, 0))
So this can be directly transposed into DAX using the equivalent functions by swapping the arguments, in DAX for Date Functions, the interval is the last argument, in SQL it is the first.
Transact-SQL
DAX
DATEDIFF (Transact-SQL)
DATEDIFF (DAX Function)
DATEDIFF ( datepart , startdate , enddate )
DATEDIFF ( <Date1>, <Date2>, <Interval> )
DATEADD (Transact-SQL)
DATEADD (DAX Function)
DATEADD ( datepart , number , date )
DATEADD( <Dates>, <NumberOfIntervals>, <Interval> )
EOMONTH (Transact-SQL)
ENDOFMONTH (DAX Function)
EOMONTH ( start_date [, month_to_add ] )
ENDOFMONTH ( <Dates> )
DAX
ENDOFMONTH(DATEADD(0, DATEDIFF(0, [date], QUARTER) +1, QUARTER)))
But if we understand the intent, we can use different functions in DAX. Taking a hint from the convoluted SQL, we could use EOMONTH with an Offset of 1, but DAX ENDOFMONTH doesn support an offset, so we use DATEADD to add a Day to the result of the ENDOFQUARTER (DAX Function) to get the equivalent result:
ENDOFMONTH(DATEADD(ENDOFQUARTER([date]), 1, DAY))

Related

Power BI: How to countrows by a category and use result to rank each category?

This should be simple, but I find it incredibly frustrating. I have a table with information on Cases, with Date Initiated, Date to Pricing, and Partner columns. I want to count the cases by Partner that were initiated within a date range, and have a Date To Pricing entry. Here's my code:
Rank Pard Cases Priced. =
VAR Table0 =
FILTER ( 'Cases',
not ISBLANK( [Date To Pricing] )
&& [Date Initiated] >= date(2021,1,1)
&& [Date Initiated] <= DATE(2021,12,31) )
VAR
Table1 =
SUMMARIZECOLUMNS(
'Cases'[Partner],
"Price_Pard",
countrows(FILTER ( 'Cases',
not ISBLANK( [Date To Pricing] )
&& [Date Initiated] >= date(2021,1,1)
&& [Date Initiated] <= DATE(2021,12,31) ) ) )
VAR
Table2 =
ADDCOLUMNS( Table1, "Rank", RANKX(Table1, [Price_Pard],, DESC, Skip))
RETURN
MINX(Table2, [Rank])
When I EVALUATE this code in DAX Studio, ending with RETURN Table2, I get exactly what I'm looking for, a table of the Partners, the counts, and the ranks.
I'm using this measure on a visual table that has the Partner name, so I expect it to give me a column with the expected ranks. However, I get the dreaded message:
I really have no idea what that means. Where does the "AddMissingItems" come from? I've been getting it a lot lately, even with measures that are simpler than this one, and don't involve any summarizing. I think the problem might be with having a FILTER inside SUMMARIZECOLUMNS apparently that's not well-supported. I've been researching this for 2 hours, and haven't found a solution, or an alternative that works. Most of what I read is confusing rather than helpful.
If I use the measure all by itself in a Card, with no Partner filter context, it works and gives me a value of 1, as expected (the min rank).
So my questions are:
(1) What does that error message mean?
(2) How can I get my code to work?
(3) If I need to restructure the query using SELECTCOLUMNS and grouping, please give me specifics. I've tried a few variations on that, but without any luck. In DAX Studio, the results are the same for every Partner: the total number of lines, and rank 1.

DAX to get previous time period, no matter if sliced by week, month, year, fiscal month, day, etc

I have created a date table in a Power BI data model that includes Date, Week, Month, Year, Fiscal Month, etc.
If I am sliced by week (the week appearing in the "Columns" section of a Power BI Matrix Visual), then I am including 7 rows of data (one for each day) from the date table. If by month, then it could be anywhere from 28 to 31 days.
I'm creating a measure that depends on calculating values for the previous slice's date range. For instance, if I am sliced to 6/15/2020 then I need to make a calculation for 6/14/2020 and if I'm sliced by Month then I need the previous month's date range, etc.
Is there a way in DAX to slice by the previous slice or to detect which field you are currently sliced by?
I would like to avoid writing a giant if statement to detect the different possibilities. I am hoping there is a generic way to retrieve all the dates in the previous slice no matter how we are sliced.
I have made use of the typical parrallelperiod, datesinperiod, datesmtd, etc. but that is not what I'm looking for.
Date tables typically have several hierarchies that could allow for use of stuff like isinscope, but since I don't want to have a giant block of code to run through the various things that could potentially be in scope, I don't think that approach will work either.
My goal is to build a single measure that would work for users building various reports no matter which date field they slice by.
Since Week isn't even a period that Time Intelligence works for (as fas as I understand), I don't think there's much hope for a clean general solution but here are a couple of imperfect approaches sketched:
1. SWITCH
PriorSlice =
VAR PriorDates =
SWITCH (
TRUE (),
ISFILTERED ( DateTable[DateKey] ), DATEADD ( DateTime[DateKey], -1, DAY ),
ISFILTERED ( DateTable[Week] ), DATEADD ( DateTime[DateKey], -7, DAY ),
ISFILTERED ( DateTable[Month] ), PARALLELPERIOD ( DateTime[DateKey], -1, MONTH ),
ISFILTERED ( DateTable[Year] ), PARALLELPERIOD ( DateTime[DateKey], -1, YEAR )
)
RETURN
CALCULATE ( [Measure], PriorDates )
(I'm not sure if ISINSCOPE would be better than ISFILTERED here or not.)
2. Shift by days in scope
PriorSlice =
VAR Shift = 1 + MAX ( DateTable[DateKey] ) - MIN ( DateTable[DateKey] )
RETURN
CALCULATE ( [Measure], DATEADD ( DateTable[DateKey], -Shift, DAY ) )
This won't really work for uneven periods like months. For example, you have February selected and want to get all of January (instead of the last 28/29 days).
Ultimately, you're going to need some kind of SWITCH either in your measure or in a helper measure if you want to work at multiple time granularities simultaneously.

DAX DateDiff with slicer

Need help with DAX Syntax in what I am trying to accomplish. Here is what I have currently tried
Number of Months = ABS(DATEDIFF(myLeas[RENTDATE],TODAY(),Month))
The Problem is, in place of "Today" i need to pass the date coming from the Slicer on the visual. How do I do this?
Replace TODAY() with:
SELECTEDVALUE ( Table[Column] )
where Table[Column] is the column you have put on the slicer.
So your measure will be:
Number of Months =
ABS (
DATEDIFF (
myLeas[RENTDATE],
SELECTEDVALUE ( Table[Column] ),
MONTH
)
)
Search for additional tips:
capture slicer value pbi
For further tips:
https://powerpivotpro.com/2018/02/using-selectedvalues-capture-power-bi-slicer-selections/
Since it is a date slicer, you probably have start and end dates. In this case SELECTEDVALUE might not work. You could try using variables in this case:
Months =
VAR MAX_DATE = MAXX(ALLSELECTED(myLeas),myLeas[RentDate])
Return ABS(DATEDIFF(Table[RentDate],MAX_DATE,MONTH))
This should give the value you are looking for. Hope this helps.

Change value to 0 for rows in future dates

Story:
Basically I'm trying to showcase the month to date compared to the parallel month along with the delta.
Usually this will only be used for the current month. I was able to add future dates but the values for month to date is repeated for all the days in the future
Objective:
I would like the value to be 0 instead of the last value being repeated for the entire future dates
Essentially I would like the following code for my measure but its not working:
_Join MTD =
IF(
TODAY()<Current Row date,
0,
TOTALMTD(SUM('Fact Table'[Join Count]),'Date Table'[Date])
)
Measures have no concept of a "current row". Measures are only affected by filter context. This can be confusing at first, because a table visual or a matrix visual has row labels, but these things are contributing filter context. The only place you will find row context is when adding a calculated column to a data table or within an iterator function.
You can probably do something like what I show below, but if you need any more help you'll need to share a sample of your data model (tables, columns, a few sample rows from each, relationships among tables) as well as the visual you're building.
_Join MTD =
IF(
TODAY () < MIN ( 'Date Table'[Date] ), // here we find the smallest day in filter
// context with MIN. If there is only one day
// in context, well then that is the min
0,
TOTALMTD ( SUM ( 'Fact Table'[Join Count] ), 'Date Table'[Date] )
)
It's a common idiom in DAX to take the MAX or MIN of a column when you want a "currently selected" value, though there are also more selective functions, such as SELECTEDVALUE if you want to guarantee that exactly one value is in filter context.
Edit: Per comments, the goal is to compare to yesterday, not today. We can simply take the measure above and modify its predicate as below:
( TODAY () - 1 ) < MIN ( 'Date Table'[Date] )
Arithmetic operators do reasonable things with date and time types in DAX. Also sometimes useful is that the DATE function deals gracefully with overflow and underflow, per the examples below:
DATE ( 2018, 1, 366 ) // => 2019-01-01
DATE ( 2018, 0, 1 ) // => 2017-12-01
DATE ( 2019, 2, 31 ) // => 2019-03-03
DATE ( 2019, 3, 0 ) // => 2019-02-28
So, if you must, you can abuse this function for date shifting as well, though it's usually a last resort.

Power BI - DAX - Bizarre Behavior for YTD calculation

Trying to use a simple year to date (YTD) measure. But things are not working as expected.
Data Model
dim_date (the weekend column is incorrect, but doesn't affect of principle)
fact
The YTD measure is defined as the following:
YTD = CALCULATE(
SUM('fact'[value]),
DATESYTD(dim_date[Date])
)
which give me the following incorrect results for YTD.
It looks like instead of calculating the YTD for the weekend and weekday separate.
Given there are16 rows with that have a date that is label as a weekend as well as fact that all of them falls in the same year, I expect the YTD and valueto be identical in the above.
Interesting enough, if I slightly change the DAX by using the dim_date[Date].[Date] instead of just dim_date[Date], everything just works as expected.
YTD = CALCULATE(
SUM('fact'[value]),
DATESYTD(dim_date[Date].[Date])
)
Can anyone help to explain what is actually going on here?
The example .pbix file is here:
https://drive.google.com/open?id=1y3ndL7yDE7T4x7Z2bPhMsa-NHRgGzYj0
As dax.guide points out,
DATESYTD ( <Dates>[, <YearEndDate>] )
is equivalent to
DATESBETWEEN (
<DATES>,
STARTOFYEAR ( LASTDATE ( <DATES> )[, <YEARENDDATE>] ),
LASTDATE ( <DATES> )
)
So what's happening is that since Jan 10 is the last False value for weekend and Jan 12 is the last True value for weekend and the DATESBETWEEN function returns a continuous range not filtered by the weekend evaluation context, you get all dates up to Jan 10 in one case and all dates up to Jan 12 in the other.
To make the measure take into account the weekend value instead of calculating over a contiguous date range, you can add that as a filter:
YTD = CALCULATE(
SUM('fact'[value]),
DATESYTD(dim_date[Date]),
dim_date[weekend] IN VALUES(dim_date[weekend])
)